【黑马程序员】 java笔记——线程通信

来源:互联网 发布:递归算法流程图 编辑:程序博客网 时间:2024/06/03 19:37
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

线程间通信

 

一、线程间通信

思考1wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

1.      这些方法存在与同步中。

2.      使用这些方法时必须要标识所属的同步的锁。

3.      锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

思考2wait(),sleep()有什么区别?

         wait():释放资源,释放锁。

         Sleep():释放资源,不释放锁。

wait()sleep()方法的异同点:

两个方法都可以让线程处于冻结状态。

sleep必须指定时间,wait可以指定时间,也可以不指定。

sleep()方法会释放执行权,不会释放锁。

wait()方法会释放执行权,会释放锁。

1.线程间通讯

线程间通讯其实就是多个线程在操作同一个资源,但是操作的动作不同。

多线程(线程间通信-等待唤醒机制)

Wait()

notify();

notifyAll();

都使用在同步中,因为要对持有监视器()的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

synchronized(obj){   obj.wait();}public synchronized void fun(){   this.wait();}public class MyClass{public staticsynchronized void fun1() {       MyClass.class.wait();}}

2.线程间通信—生产者消费者

无论是同步函数,还是同步代码块,所做的都是隐式操作。

同步函数或同步代码块,使用的锁和监视器是同一个。

Lock接口:是将锁进行单独对象的封装。而且提供了对锁对象的很多功能。

比如lock()获取锁, unlock()释放锁。

Lock对锁的操作都是显示操作。

所以它的出现要比同步函数或者同步代码块明确的多。而且更符合面向对象思想。

简单一句话:Lock接口的出现替代了同步。

好处:将同步当中的隐式锁对象封装成了显示锁操作。

以前的锁是任意对象,现在的是指定对象Lock,Lock时必须写finally

//生产者class Producer implements Runnable{         privateResource r;         Producer(Resourcer)         {                   this.r= r;         }         publicvoid run()         {                   while(true)                   {                   r.set("馒头");                   }         }}//消费者class Consumer implements Runnable{         privateResource r;         Consumer(Resourcer)         {                   this.r= r;         }         publicvoid run()         {                   while(true)                   {                   r.get();                   }         }}

原来在同步中,锁和监视器是同一个对象。

现在升级后,锁是一个单独的对象。

而且将监视器的方法也单独封装到了一个对象中,这个对象就是升级后的condition,升级后,都进行了单独的封装。

锁被封装成了Lock对象。

监视器方法都被封装到了Condition对象(监视器对象)中。

说白了,Lock替代了同步中的同步,Conditon替代了Object中的监视器方法。

Condition中提供了监视器的方法:await(),signal(),signalAll()

如何让锁和监视器产生联系?

直接通知Lock接口中的newCondition()方法就可以获取到能绑定到该Lock对象上的监视器对象Condition

代码:

class ProConDemo3{         publicstatic void main(String[] args)         {         Resourcer = new Resource();         Producerpro = new Producer(r);         Consumercon = new Consumer(r);         //两个线程负责生产。         Threadt0 = new Thread(pro);         Threadt1 = new Thread(pro);         //两个线程负责消费。         Threadt2 = new Thread(con);         Threadt3 = new Thread(con);         t0.start();         t1.start();         t2.start();         t3.start();         }}

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

JDK1.5中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

Object中的waitnotify  notifyAll,替换了condition对象。

该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。

二、线程间通讯总结

         使用wait()notify()notifyAll()方法可以完成线程间的通讯,可叫它们通讯方法;

         只能在同步环境下调用通讯方法;

         只能使用监视器对象调用通讯方法;

         每个监视器对象都有一个线程监狱:执行a.wait()的线程会被关押到a对象的线程监狱中;

         若想释放出a对象的线程监狱中的线程,那么需要调用a.notify()文法,该方法只能保证在a对象的线程监狱中释放出一个线程,但不能保证释放的是哪一个;

         还可以使用a.notifyAll()方法释放出a对象的监狱中关押的所有线程。

         wait()了的线程不能自己恢复到就绪状态,只能等待其他线程调用同一监视器对象上的notify()notifyAll()方法来唤醒。

         wait()了的线程会释放监视器对象的对象锁,这样其他线程就可以进入他占用的同步环境。

         被唤醒的线程恢复到了就绪状态,当再次获取监听器对象的锁后会在wait()处向下运行。

1.停止线程

1.定义循环结束标记

         因为线程运行代码一般都是循环,只要控制了循环即可。

2.使用interrupt(中断)方法。

         该方法是结束线程的冻结状态,使线程回到运行状态中来。

注:stop方法已经过时不再使用。

stop方法已经过时。

如何停止线程?

只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。

只要控制循环,就可以让run方法结束,也就是线程结束。

public void run(){while(true){System.out.println("ok");}}

2.特殊情况

当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供该方法interrupt();

如果读不到标记怎么办?

比如在任务中让线程处于了冻结状态。

释放了执行资格,无法执行标记,run方法没结束,线程也无法结束。

Thread类中有一个interupt方法。可以将线程的冻结状态清除,让线程恢复到具备执行资格。

例:

class StopThread implements Runnable{private booleanflag = true;publicsynchronized void run(){while(flag){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...."+e.toString());flag = false;}System.out.println(Thread.currentThread().getName()+"......run");}}public voidsetFlag(){flag = false;}}class StopThreadDemo{public staticvoid main(String[] args){StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.start();t2.start();int num = 1;while(true){if(++num==50){// st.setFlag();//将标记置为false。让t1,t2对应的线程结束。//清除t1的冻结状态。t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"....."+num);}System.out.println("over");}}

使用interrupt()方法:

中断方法:interrupt()

Thread类中有一个boolean类型的属性,我们叫中断值,这个中断值默认为false

当调用了线程对象的interrupt()方法后,这时中断值为true

所有的声明了InterruptedException异常的方法都会在中断值为true时抛出这个异常。

Thread.sleep()

Object.wait()

Thread.join()

当这些方法抛出异常之后,中断值会恢复到false

 

isInterrupted()方法和interrupted()的作用:

isInterrupted() –实例方法

  获取该线程的中断状态,该方法不会去修改中断值,只是对中断值进行读操作。

interrupted() –静态方法

  获取当前线程的中断状态,该方法会在获取中断值之后,把中断值修改为false。然后返回修改之后获取到的值。boolean b =中断值;中断值=false;return b

interrupt() –实例方法 

把当前中断值设置为true

 

setDaemon()将线程标记为守护线程或用户线程(后台线程),一定要在start之前调用。

多线程(Join方法)

A线程执行到了B线程的join()方法时,A就会等待,等B线程都执行完,A才会执行。

Join可以用来临时加入线程执行。

join() 等待该线程终止。当前线程等待调用此方法的线程执行完才执行。

凡是让线程处于中断的方法都需要抛出 InterruptedException异常。

join()方法的作用:

public class Test{  public static void main(String[] args) throws InterruptedException   {      Thread th = new Thread()         {         public void run()           {              for(int i = 0; i < 10; i++)                   {                 System.out.println(i);                 try {                            Thread.sleep(500);                     }catch(InterruptedException e) {}                   }         }      }      th.start();      th.join();       //让当前线程(主线程)等待该线程(th)结束,再向下执行。      System.out.println("程序结束!!!");   }}

让当前线程等待该线程结束再向下运行。

当前线程:语句是谁执行的,谁就是当前线程。

该线程:你调用了哪个线程对象的方法,哪个线程就是该线程。

 

多线程(优先级与yield方法)

yield()临时暂停线程,其为静态方法。

yield()方法的作用:

Thread.yield();     //暂停当前正在执行的线程对象,并执行其他线程。

当前线程执行了这条语句,表示当前线程已经完成最重要的部分,然后跟调度说,我可以休息了,你可以去为其他线程服务了,但当前线程不会进入阻塞状态,还是就绪状态。

让步不同与阻塞,让步之后,线程还是就绪状态。


---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net
原创粉丝点击