【黑马程序员】 java笔记——线程通信
来源:互联网 发布:递归算法流程图 编辑:程序博客网 时间:2024/06/03 19:37
线程间通信
一、线程间通信
思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
1. 这些方法存在与同步中。
2. 使用这些方法时必须要标识所属的同步的锁。
3. 锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
思考2:wait(),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中的wait,notify 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(); //暂停当前正在执行的线程对象,并执行其他线程。
当前线程执行了这条语句,表示当前线程已经完成最重要的部分,然后跟调度说,我可以休息了,你可以去为其他线程服务了,但当前线程不会进入阻塞状态,还是就绪状态。
让步不同与阻塞,让步之后,线程还是就绪状态。
- 【黑马程序员】 java笔记——线程通信
- 黑马程序员—11—java基础:有关线程通信的学习笔记和学习心得体会
- 黑马程序员——Java基础_线程通信
- 黑马程序员——Java基础---线程间通信
- 黑马程序员——线程通信
- 黑马程序员——线程通信
- 黑马程序员——线程间通信
- 黑马程序员——java基础拾遗之多线程(二) 线程同步、线程通信
- 黑马程序员——JAVA笔记——线程
- 黑马程序员笔记——Java多线程线程间通讯
- 黑马程序员——JAVA笔记之多线程
- 黑马程序员——java线程学习笔记
- 黑马程序员—Java基础学习笔记之多线程
- 黑马程序员——JAVA基础——线程---概述,创建、生命周期,控制,同步,线程通信
- 黑马程序员:JAVA线程间的通信
- 黑马程序员之java线程通信学习
- 黑马程序员——Java语言基础——04.多线程(2)线程间通信
- 黑马程序员——JAVA基础---线程之间的通信实现交替输出打印
- GRAILS中Criteria的OR的写法
- (转)CvMakeHistHeaderForArray cvCreateHist
- JVM性能优化
- 《UNX编程艺术》之命令行选项的常见约定
- cocos2d-x2.2跨平台环境搭建
- 【黑马程序员】 java笔记——线程通信
- 使用doxygen生成的帮助文档,中文出现乱码的问题
- Grails在Eclipse中的安装
- GFS笔记
- 慎用PHP $_REQUEST数组
- Settings UI样式调整
- Java MessageFormat.format格式化字符串大括号以及单引号问题
- Servlet类找不到问题
- grails创建第一个属于你的项目实战(图文)