线程协作

首先引入一段代码:

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class Watch {
  4. private static String time;
  5. static class Display extends Thread{
  6. Time timeThread;
  7. Display(Time time) {
  8. this.timeThread=time;
  9. }
  10. @Override
  11. public void run() {
  12. if(time==null){
  13. }
  14. System.out.println(time);
  15. }
  16. }
  17. static class Time extends Thread{
  18. @Override
  19. public void run() {
  20. time = new Date().toString();
  21. }
  22. }
  23. public static void main(String[] args) {
  24. Time time = new Time();
  25. time.start();
  26. new Display(time).start();
  27. }
  28. }

结果显示为:

之后我们在if(time==null){ }语句代码块中添加,使其能够正常输出(输出结果不为null)
代码展示为:

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class Watch {
  4. private static String time;
  5. static class Display extends Thread{
  6. Time timeThread;
  7. Object object;
  8. Display(Time time) {
  9. this.timeThread=time;
  10. }
  11. @Override
  12. public void run() {
  13. if(time==null) {
  14. try {
  15. sleep(1500);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. System.out.println(time);
  21. }
  22. }
  23. static class Time extends Thread{
  24. @Override
  25. public void run() {
  26. time = new Date().toString();
  27. }
  28. }
  29. public static void main(String[] args) {
  30. final Object OBJECT = new Object();
  31. Time time = new Time();
  32. time.start();
  33. new Display(time).start();
  34. }
  35. }

结果显示为:

我们通过sleep()方法实现了正常的输出,但是在实际操作过程中,我们无法判断sleep()中所需要限制的时间,因此我们需要通过另一种方法来实现:即使用join()方法,代码展示为:

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class Watch {
  4. private static String time;
  5. static class Display extends Thread{
  6. Time timeThread;
  7. Object object;
  8. Display(Time time) {
  9. this.timeThread=time;
  10. }
  11. @Override
  12. public void run() {
  13. if(time==null) {
  14. try {
  15. timeThread.join();//线程A执行“已死”线程B所调用的jion方法,则线程A不会阻塞。
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. System.out.println(time);
  21. }
  22. }
  23. static class Time extends Thread{
  24. @Override
  25. public void run() {
  26. time = new Date().toString();
  27. }
  28. }
  29. public static void main(String[] args) {
  30. final Object OBJECT = new Object();
  31. Time time = new Time();
  32. time.start();
  33. new Display(time).start();
  34. }
  35. }

结果显示为:

如果不使用join()方法,我们还可以选择使用synchronized ()关键字的形式实现,代码展示为:

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class Watch {
  4. private static String time;
  5. static class Display extends Thread{
  6. Time timeThread;
  7. Object object;
  8. Display(Time time,Object object) {
  9. this.timeThread=time;
  10. this.object=object;
  11. }
  12. @Override
  13. public void run() {
  14. if(time==null) {
  15. synchronized (object) {
  16. try {//如果没有synchronized关键字,则会出现报错
  17. object.wait();//线程执行该方法,则该线程阻塞,知道调用notify方法
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  23. System.out.println(time);
  24. }
  25. }
  26. static class Time extends Thread{
  27. Object object;
  28. Time(Object object){
  29. this.object=object;
  30. }
  31. @Override
  32. public void run() {
  33. time = new Date().toString();
  34. synchronized (object) {
  35. object.notify();
  36. }
  37. }
  38. }
  39. public static void main(String[] args) {
  40. final Object OBJECT = new Object();
  41. Time time = new Time(OBJECT);
  42. time.start();
  43. new Display(time,OBJECT).start();
  44. }
  45. }

结果显示为:

Object类中解决线程间协作的方法

synchronized关键字只是起到了多个线程“串行”执行临界区中代码的作用,但是哪个线程先执行,哪个线程后执行依无法确定。Object类中的wait()、notify()和notifyAll()三个方法解决了线程间的协作问题,通过这三个方法的“合理”使用可以确定多线程中线程的先后执行顺序:
1、wait():对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,直到在其他线程中该对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒。
2、notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程。
3、notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线、程;调用notifyAll()方法并不会立即激活某个等待线程,它只能撤销等待线程的中断状态,这样它们就能够在当前线程退出同步方法或同步代码块法后与其它线程展开竞争,以争取获得资源对象来执行。

谁调用了wait方法,谁就必须调用notify或notifyAll方法,并且“谁”是对象锁。

使用Object类中的wait()、notify()和notifyAll()三个方法需要注意以下几点:
1、wait()方法需要和notify()或notifyAll()方法中的一个配对使用,且wait方法与notify()或notifyAll()方法配对使用时不能在同一个线程中(参见代码1)。
2、wait()方法、notify()方法和notifyAll()方法必须在同步方法或者同步代码块中使用,否则出现IllegalMonitorStateException 异常。
3、调用wait()方法、notify()方法和notifyAll()方法的对象必须和同步锁对象是一个对象。

sleep()方法和wait()方法区别

1、sleep()方法被调用后当前线程进入阻塞状态,但是当前线程仍然持有对象锁,在当前线程sleep期间,其它线程无法执行sleep方法所在临界区中的代码。

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class sleepwait区别 {
  4. public static void main(String[] args) {
  5. Object lockObj = new Object();
  6. new PrintThread("1号打印机"+new Date(),lockObj).start();
  7. new PrintThread("2号打印机"+new Date(),lockObj).start();
  8. }
  9. }
  10. class PrintThread extends Thread {
  11. private Object lockObj;
  12. public PrintThread(String threadName, Object lockObj) {
  13. super(threadName);
  14. this.lockObj = lockObj;
  15. }
  16. @Override
  17. public void run() {
  18. synchronized (lockObj) {
  19. System.out.println(getName());
  20. try {
  21. sleep(3000);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

结果显示为:

由此我们可以看出:当某台打印机执行临界区中的代码,输出线程名后由于调用了sleep方法,从而使得该打印机线程阻塞3秒,在这3秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在3秒后才能执行临界区中的代码。(但是所执行的时间是相同的)
2、对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,在当前线程处于线程等待期间,其它线程可以执行wait方法所在临界区中的代码。

  1. package 线程间数据共享;
  2. import java.util.Date;
  3. public class sleepwait区别 {
  4. public static void main(String[] args) {
  5. Object lockObj = new Object();
  6. new PrintThread("1号打印机"+new Date(),lockObj).start();
  7. new PrintThread("2号打印机"+new Date(),lockObj).start();
  8. }
  9. }
  10. class PrintThread extends Thread {
  11. private Object lockObj;
  12. public PrintThread(String threadName, Object lockObj) {
  13. super(threadName);
  14. this.lockObj = lockObj;
  15. }
  16. @Override
  17. public void run() {
  18. synchronized (lockObj) {
  19. System.out.println(getName());
  20. try {
  21. lockObj.wait();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

结果显示为:

wait()方法

代码一:

  1. package 线程间数据共享;
  2. class CounterThread extends Thread {
  3. private Object lockObj;
  4. public CounterThread(String threadName, Object lockObj) {
  5. super(threadName);
  6. this.lockObj = lockObj;
  7. }
  8. @Override
  9. public void run() {
  10. int i = 1;
  11. while (true) {
  12. synchronized (lockObj) {
  13. System.out.println(getName() + ":" + i);
  14. try {
  15. lockObj.wait();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. i++;
  20. }
  21. }
  22. }
  23. }
  24. public class Test {
  25. public static void main(String[] args) {
  26. Object lockObj = new Object();
  27. new CounterThread("1号计数器",lockObj).start();
  28. new CounterThread("2号计数器",lockObj).start();
  29. }
  30. }

结果显示为:

尽管while(){xxx}循环是死循环,但由于对象锁lockObj调用了wait()方法,使得分别持有该lockObj对象锁的“1号计数器”线程和“2号计数器”线程处于线程等待状态,所以循环并没有继续下去
代码二:

  1. package 线程间数据共享;
  2. import java.text.DateFormat;
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. public class sleepwait区别 {
  6. public static void main(String[] args) {
  7. TimeThread timeThread = new TimeThread ();
  8. timeThread.start();
  9. synchronized (timeThread) {
  10. try {
  11. timeThread.wait();
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. System.out.println("main方法");//由于主线程进入了阻塞状态,所以该行代码不执行
  17. }
  18. }
  19. class TimeThread extends Thread{
  20. @Override
  21. public void run() {
  22. DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
  23. while (true) {
  24. String currentTime = dateFormat.format(new Date());
  25. System.out.println(currentTime);
  26. try {
  27. sleep(1000);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. }

结果显示为:

为什么时间线程没有进入阻塞状态呢?
timeThread变量所代表的对象充当对象锁,由于该代码在main方法中,也就是说将来由主线程执行临界区中的代码,也就是说主线程是持有对象锁的线程,主线程执行完“timeThread.wait()”代码后进入阻塞状态,是主线程进入阻塞状态而非时间线程,这也是main方法中“System.out.println(“main方法”);”代码不执行的原因。

notify()方法和notifyAll()方法

notify()方法

  1. import java.text.SimpleDateFormat;
  2. import java.util.Date;
  3. public class ElectronicWatch {
  4. String currentTime;
  5. DisplayThread displayThread = new DisplayThread();
  6. private static final Object lockObject = new Object();
  7. public static void main(String[] args) {
  8. new ElectronicWatch().displayThread.start();
  9. }
  10. /**
  11. * 该线程负责显示时间
  12. */
  13. class DisplayThread extends Thread{
  14. @Override
  15. public void run() {
  16. synchronized (lockObject) {
  17. new TimeThread().start();
  18. try {
  19. lockObject.wait();
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println(currentTime);
  24. }
  25. }
  26. }
  27. /**
  28. * 该线程负责获取时间
  29. */
  30. class TimeThread extends Thread{
  31. @Override
  32. public void run() {
  33. synchronized (lockObject) {
  34. try {
  35. sleep(3000);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. String pattern = "yyyy-MM-dd HH:mm:ss";
  40. SimpleDateFormat sdf = new SimpleDateFormat(pattern);
  41. currentTime = sdf.format(new Date());
  42. lockObject.notify();
  43. }
  44. }
  45. }
  46. }

结果显示为:

notifyAll()方法

  1. package 线程间数据共享;
  2. class CounterThread extends Thread {
  3. private Object lockObj;
  4. public CounterThread(String threadName, Object lockObj) {
  5. super(threadName);
  6. this.lockObj = lockObj;
  7. }
  8. @Override
  9. public void run() {
  10. int i = 1;
  11. while (true) {
  12. synchronized (lockObj) {
  13. System.out.println(getName() + ":" + i);
  14. try {
  15. lockObj.wait();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. i++;
  20. }
  21. }
  22. }
  23. }
  24. class NotifyAllThread extends Thread {
  25. private Object lockObj;
  26. public NotifyAllThread(Object lockObj) {
  27. this.lockObj = lockObj;
  28. }
  29. @Override
  30. public void run() {
  31. synchronized (lockObj) {
  32. System.out.println("notifyAll方法执行完毕");
  33. lockObj.notifyAll();
  34. }
  35. }
  36. }
  37. public class Test {
  38. public static void main(String[] args) {
  39. Object lockObj = new Object();
  40. new CounterThread("1号计数器", lockObj).start();
  41. new CounterThread("2号计数器", lockObj).start();
  42. try {
  43. Thread.sleep(5000);
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. new NotifyAllThread(lockObj).start();
  48. }
  49. }

结果显示为:

  1. 如果调用的是notify方法,则只会唤醒在lockObj对象锁上等待的两个线程中的一个;
  2. 而调用notifyAll方法则会全部唤醒,尽管只调用一次
    http://www.dtmao.cc/news_show_676906.shtml

Java中详述线程间协作的更多相关文章

  1. java中关于线程间协作所用关键字synchronized,wait,notify的用法

    wait/notify()关键字适用于一个线程通知另一个线程所需的条件状态已就绪,最常用于线程在循环中休眠直到获取特定条件的场景. 例如,一个线程一直等待直到队列中有一个组件能够处理:当组件添加到队列 ...

  2. java并发编程 线程间协作

    线程间协作 1. 等待和通知 等待和通知的标准形式 等待方: 获取对象锁 循环中判断条件是否满足,不调用wait()方法 条件满足执行业务逻辑 通知方: 获取对象所 改变条件 通知所有等待在对象的线程 ...

  3. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  4. Java并发编程(十三)线程间协作的两种方式:wait、notify、notifyAll和Condition

    在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果 ...

  5. Java多线程之线程的状态以及线程间协作通信导致的线程状态转换

      转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561589.html  一:线程的状态以及变化图 Java中线程中状态可分为五种:New(新建状态),Ru ...

  6. Java并发编程(八)线程间协作(上)

    多线程并发执行时,不同的线程执行的内容之间可能存在一些依赖关系,比如线程一执行a()方法和c()方法,线程二执行b()方法,方法a()必须在方法b()之前执行,而方法c()必须在方法b()之后执行.这 ...

  7. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  8. Java并发--线程间协作的两种方式:wait、notify、notifyAll和Condition

    在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界 ...

  9. java并发之线程间通信协作

    在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界 ...

随机推荐

  1. Java 基于 mysql-connector-java 编写一个 JDBC 工具类

    用到的 jar 包 jar包地址: mysql-connector-java-5.1.47.jar junit-4.13.jar Maven: <!-- mysql驱动 --> <d ...

  2. kali中安装漏洞靶场Vulhub

    一.什么是vulhub? Vulhub是一个基于docker和docker-compose的漏洞环境集合,进入对应目录并执行一条语句即可启动一个全新的漏洞环境,让漏洞复现变得更加简单,让安全研究者更加 ...

  3. 【MYSQL】MySQL5.6.37二进制安装

    最近有个项目要用到mysql 于是在mysql的论坛中找到了一个5.6.37版本的 下面介绍怎么安装和使用mysql 下载地址: https://dev.mysql.com/downloads/mys ...

  4. 【ORA】ORA-27090: Unable to reserve kernel resources for asynchronous disk I/O

    操作系统是CentOS 5.11 数据库 10.2.0.5.0 晚上查看数据库,发现数据库报错查看相关的trace文件内容如下: *** SERVICE NAME:(SYS$BACKGROUND) 2 ...

  5. LeetCode543.二叉树的直径

    题目 1 class Solution { 2 public: 3 int minimum = INT_MIN; 4 vector<int>res; 5 int diameterOfBin ...

  6. ctfhub技能树—web前置技能—http协议—302跳转

    开启靶机 打开环境,查看显示 点击Give me Flag后发生跳转 根据题目提示为HTTP临时重定向 简单记录一下HTTP临时重定向是什么 HTTP重定向:服务器无法处理浏览器发送过来的请求(req ...

  7. 过压保护IC和带LDO模式的Li+充电器前端保护IC

    PW2601是一种充电器前端集成电路,旨在为锂离子提供保护电池充电电路故障.该设备监测输入电压,电池电压以及充电电流,以确保所有三个参数都在正常范围内工作.这个该设备将关闭内部MOSFET断开,以保护 ...

  8. 2V升3V芯片,输入2V输出3V可达1A

    PW5328B是一个恒定频率, 6引脚 SOT23电流模式升压转换器,用于小型低功耗应用. PW5328B的开关频率为 1.2MHz,允许使用微小的.低成本的电容器和电感器.内部软启动导致小涌流和延长 ...

  9. 关于springboot项目通过jar包启动之后无法读取项目根路径静态资源

    在一次项目开发过程中,项目根路径下存放了一张图片,生成二维码的时候调用了该图片作为二维码的logo,在windows环境下二维码可以正常生成,但是部署到生产测试环境之后二维码生成报错,FileNotF ...

  10. 图解ARP协议

    前置知识:MAC地址 在说到ARP协议之前,需要了解MAC地址,在OSI七层模型中,第三层是网络层,要解决的是下一跳机制确定的设备位置,具体来说就是网卡地址,MAC地址用于在网络中唯一标示一个网卡,一 ...