您的位置:首页 > 职场人生

黑马程序员_java多线程与并发库高级应用

2014-01-13 12:58 736 查看
------- android培训java培训、期待与您交流!
----------

一、传统线程技术

传统是相对于JDK1.5而言的

传统线程技术与JDK1.5的线程并发库

线程就是程序的一条执行线索/线路。

创建线程的两种传统方式
1)创建Thread的子类,覆盖其中的run方法,运行这个子类的start方法即可开启线程

2)创建Thread时传递一个实现Runnable接口的对象实例
总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。
多线程并不一定会提高程序的运行效率。
多线程下载:并不是自己电脑快了,而是抢到更多服务器资源。
3)当在Thread子类中覆盖了run方法并编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么线程在运行时执行的是子类所覆盖的run方法。
1).public staticvoid main(String[] args) {

Threadthread = new Thread(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1:" +Thread.currentThread().getName());
System.out.println("2:" +this.getName());
}
}
};
thread.start();
2).Threadthread2 = new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1:" +Thread.currentThread().getName());
}

}
});
thread2.start();
3).new Thread(
newRunnable(){
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("runnable :"+ Thread.currentThread().getName());
}
}
}
){
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread :" +Thread.currentThread().getName());
}

}
}.start();
}

二、传统定时器
传统定时器的创建:直接使用定时器类Timer

a、过多长时间后炸

newTimer().schedule(TimerTask定时任务, Date time定的时间);

b、过多长时间后炸,以后每隔多少时间再炸

newTimer().schedule(TimerTask定时任务, Long延迟(第一次执行)时间,
Long间隔时间);

TimerTask与Runnable类似,有一个run方法

Timer是定时器对象,到时间后会触发炸弹(TimerTask)对象

注意:内部类中不能声明静态变量

定义一个静态变量private static count = 0;

在run方法内部:count=(count+1)%2;

将定时器的时间设置为:2000+2000*count

示例:
private static int count = 0;

public static voidmain(String[] args) {

class MyTimerTask extends TimerTask{

@Override

public void run(){

count = (count+1)%2;

System.out.println("bombing!");

new Timer().schedule(newMyTimerTask(),2000+2000*count);

}

}

new Timer().schedule(new MyTimerTask(), 2000);

while(true){

System.out.println(newDate().getSeconds());

try {

Thread.sleep(1000);

} catch(InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

定时器还可以设置具体时间。

三、传统线程互斥技术

需要注意的地方:

内部类不能访问局部变量,要访问需加final。

静态方法中不能创建内部类的实例对象。

互斥方法:

同步代码块:synchronized (lock){}

同步方法:方法返回值前加synchronized,同步方法上边用的锁就是this对象,静
态同步方法使用的锁是该方法所在的class文件对象

使用synchronized关键字实现互斥,要保证同步的地方使用的是同一个锁对象。

四、传统线程同步通信技术

小总结:要用到共同数据(包括同步锁)或相同算法的多个方法要封装在一个类中,锁是上在代表要操作的资源类的内部方法中的,而不是上在线程代码中的。这样写出来的类就是天然同步的,只要使用的是同一个new出来的对象,那么这个对象就具有同步互斥特性。

判断唤醒等待标记时使用while增加程序健壮性,防止伪唤醒。

五、ThreadLocal实现线程范围的共享变量
JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于上一节中的Map。

importjava.util.Map;

importjava.util.Random;

public classThreadLocalTest {

private static ThreadLocal<Integer>x = new ThreadLocal<Integer>();

private staticThreadLocal<MyThreadScopeData> myThreadScopeData =

new ThreadLocal<MyThreadScopeData>();

public static void main(String[] args) {

for(inti=0;i<2;i++){

new Thread(new Runnable(){

@Override

publicvoid run() {

intdata = new Random().nextInt();

System.out.println(Thread.currentThread().getName() + " has put data :" + data);

x.set(data);

MyThreadScopeData.getThreadInstance().setName("name"+ data);

MyThreadScopeData.getThreadInstance().setAge(data);

new A().get();

new B().get();

}

}).start();

}

}

static class A{

publicvoid get(){

int data = x.get();

System.out.println("Afrom " + Thread.currentThread().getName()+ " get data :" + data);

MyThreadScopeData myData =MyThreadScopeData.getThreadInstance();

System.out.println("A from " +Thread.currentThread().getName()

+ " getMyData: " +myData.getName() + "," +

myData.getAge());

}

}

六、java原子性操作类的应用

Java5的线程并发库

java.util.concurrent在并发编程中很常用的实用工具类。

|----locks为锁和等待条件提供一个框架的接口和类,

它不同于内置同步和监视器

|----atomic类的小工具包,支持在单个变量上解除锁的线程安全编程。

可以对基本类型、数组中的基本类型、类中的基本类型等进行操作

|----AtomicInteger

七、java线程并发库的应用

1.线程池:先创建多个线程放在线程池中,当有任务需要执行时,从线程池中找一个空闲线程执行任务,任务完成后,并不销毁线程,而是返回线程池,等待新的任务安排。

2.线程池编程中,任务是提交给整个线程池的,并不是提交给某个具体的线程,而是由线程池从中挑选一个空闲线程来运行任务。一个线程同时只能执行一个任务,可以同时向一个线程池提交多个任务。

3.线程池创建方法:

(1)创建一个拥有固定线程数的线程池。

ExecutorService threadPool =Executors.newFixedThreadPool(3);

(2)创建一个缓存线程池。

ExecutorService threadPool =Executors.newCacheThreadPool();

(3)创建一个只有一个线程的线程池。

ExecutorService threadPool =Executors.newSingleThreadExector();

(4)往线程池中添加任务。

threadPool.executor(Runnable)

(5)关闭线程池。

threadPool.shutdown() 线程全部空闲,没有任务就关闭线程池

threadPool.shutdownNow() 不管任务有没有做完,都关掉

八、 Callable与Future的应用:

1)Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
2)Callable要采用ExecutorService的submit方法提交,返回的future对象可以取消任务。
3)CompletionService用于提交一组的Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
示例如下:

publicclass CallableAndFuture {
public static void main(String[] args) {
ExecutorServicethreadPool = Executors.newSingleThreadExecutor();
Future<String>future =
threadPool.submit(
newCallable<String>() {
public String call() throws Exception {
Thread.sleep(2000);
return"hello";
};
}
);
System.out.println("等待结果");
try{
System.out.println("拿到结果:"
+ future.get());
}catch (InterruptedException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}

九、java5的线程锁技术
总结:1.Lock比传统线程模型中的synchronized更加面向对象,锁本身也是一个对象,两个线程执行的代码要实现同步互斥效果,就要使用同一个锁对象。锁要上在要操作的资源类的内部方法中,而不是线程代码中。

2.锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally或 try-catch
加以保护,以确保在必要时释放锁。

十、Condition的应用

1.Condition的功能类似在传统线程技术中的Object.wait()和Object.natify()的功能,传统线程技术实现的互斥只能一个线程单独干,不能说这个线程干完了通知另一个线程来干,Condition就是解决这个问题的,实现线程间的通信。

2.Condition实例实质上被绑定到一个锁上。要为特定

Lock 实例获得 Condition实例,请使用其
newCondition()方法。

十一、Lock和Condition的应用

static class Business {

Locklock = new ReentrantLock();

Conditioncondition1 = lock.newCondition();

Conditioncondition2 = lock.newCondition();

Conditioncondition3 = lock.newCondition();

private int shouldSub = 1;

public void sub2(int i){

lock.lock();

try{

while(shouldSub != 2){

try {

condition2.await();

}catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

for(intj=1;j<=10;j++){

System.out.println("sub2 threadsequence of " + j + ",loop of " + i);

}

shouldSub = 3;

condition3.signal();

}finally{

lock.unlock();

}

}

public void sub3(int i){

lock.lock();

try{

while(shouldSub != 3){

try {

condition3.await();

}catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

for(intj=1;j<=20;j++){

System.out.println("sub3 threadsequence of " + j + ",loop of " + i);

}

shouldSub = 1;

condition1.signal();

}finally{

lock.unlock();

}

}

public void main(int i){

lock.lock();

try{

while(shouldSub != 1){

try{

condition1.await();

} catch (Exception e) {

e.printStackTrace();

}

}

for(intj=1;j<=100;j++){

System.out.println("main threadsequence of " + j + ",loop of " + i);

}

shouldSub= 2;

condition2.signal();

}finally{

lock.unlock();

}

}

十二、Semphore:实现信号灯,通常用于限制可以访问的某些资源的线程数目

Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。

Semaphore通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。

Semaphore只对可用许可的号码进行计数,并采取相应的行动。

Semphoresp = new Semphore(6);
sp.availablePermits();
sp.acquire();//请求
sp.release();//释放,添加一个许可,从而可能释放一个正在阻塞的获取者

总结:传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release() sp.acquire()。

十三、CyclicBarrier

集合等待,他允许一组线程互相等待,直到到达某个公共屏障点。

CyclicBarrier cb = newCyclicBarrier(3);
cb.await();//到了其他人没来就等

cb.getNumberWaiting();//当前等待数量
总结:CyclicBarrier支持一个可选的
Runnable
命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。

十四、CountDownLatch同步工具
倒计时计数器,调用CountDownLatch对象的countDown方法,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,他允许一个或多个线程等待。

十五、Exchanger:数据交换
用于实现两个数据之间的交换。

exchanger.eachange(data)//将自己的数据交换出去
exchange():等待另一个线程到达此交换点,然后将给定的线程传送给该线程,并接受该线程的对象。

十六、ArrayBlockingQueue:可阻塞的队列
BlockingQueue queue = newArrayBlockingQueue(3);
queue.put(1);//如果放满就会堵塞
queue.take();//移除元素

BlockingQueue方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null或
false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:

抛出异常

特殊值

阻塞

超时

插入

add(e)

offer(e)

put(e)

offer(e, time, unit)

移除

remove()

poll()

take()

poll(time, unit)

检查

element()

peek()

不可用

不可用

总结:

BlockingQueue不接受 null元素。

BlockingQueue实现主要用于生产者-使用者队列,但它另外还支持Collection接口。

BlockingQueue实现是线程安全的。

------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: