多线程并发编程(四):多线程同步互斥Wait/Notify 旧城等待, 2022-07-30 15:27 384阅读 0赞 # 前言 # > 前面说了使用Synchronized来进行线程之间的同步,接下来说明wait/notify的使用。 > > **首先wait/notify必须结合synchronized来使用,即在synchronized内部使用** > > **wait表示在获取到该对象锁之后,主动释放该对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作** > > 但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是**在相应的synchronized()\{\}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行**。这样就提供了在线程间同步、唤醒的操作。 > > Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,**主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。Thread.sleep() 并没有释放对象锁,只是暂时休眠,他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。** # 经典面试题 # ## 子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再主线程循环100次,如此循环50次,请写出程序 ## package test01; public class TraditionalThreadCommunication { public static void main(String[] args) { final Business business = new Business(); // 子线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { business.sub(i); } } }).start(); // main方法主线程 for (int i = 0; i < 50; i++) { business.main(i); } } } /** * 线程业务处理类 * @author Administrator * */ class Business{ // 子线程是否可以调用 private boolean subShould = true; // 子线程业务方法 public synchronized void sub(int i){ while(!subShould){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 10; j++) { System.out.println("sub thread sequence of " + j + " ,loop of " + i); } subShould = false; this.notify(); } // 主线程业务方法 public synchronized void main(int i){ while(subShould){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 100; j++) { System.out.println("main thread sequence of " + j + " ,loop of " + i); } subShould = true; this.notify(); } } > 分析: > 1、实现时首先考虑将线程业务处理封装成一个类,在这个类中实现同步互斥的机制,好处是那么在其他地方调用的时候就不需要考虑线程安全的问题了,另外也好实现同步机制。 > 2、拆分业务方法,分析题目可以知道,线程业务方法主要是两个,一个主线程100次,一个子线程10次,循环50次那个不属于该线程的业务方法,由调用端去循环。 实现: > 1、第一步,封装一个线程业务处理类:Business,分别提供两个业务方法,子线程打印方法和主线程打印方法,主类main方法分别实现两个线程调用这个类的两个方法,这里main方法就是主线程了,不需要另外创建一个新的线程了,所以只要创建一个线程就可以了,当然以此递推,如果需要三个线程,创建两个就可以了。所以一开始代码如下: package test01; public class TraditionalThreadCommunication { public static void main(String[] args) { final Business business = new Business(); // 子线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { business.sub(i); } } }).start(); // main方法主线程 for (int i = 0; i < 50; i++) { business.main(i); } } } /** * 线程业务处理类 * @author Administrator * */ class Business{ // 子线程业务方法 public void sub(int i){ for (int j = 0; j < 10; j++) { System.out.println("sub thread sequence of " + j + " ,loop of " + i); } } // 主线程业务方法 public void main(int i){ for (int j = 0; j < 100; j++) { System.out.println("main thread sequence of " + j + " ,loop of " + i); } } } > 这样的代码执行起来,子线程和主线程打印都是根据CPU随机分配的,我们需要将子线程与主线程进行同步,即子线程运行打印的时候,主线程不能运行打印,主线程运行打印的时候,子线程不能运行打印。那么将两个方法进行同步就可以解决这个问题,如下: class Business{ // 子线程业务方法 public synchronized void sub(int i){ for (int j = 0; j < 10; j++) { System.out.println("sub thread sequence of " + j + " ,loop of " + i); } } // 主线程业务方法 public synchronized void main(int i){ for (int j = 0; j < 100; j++) { System.out.println("main thread sequence of " + j + " ,loop of " + i); } } } > 两个方法都加上synchronized的,都是用的Business这个类的对象锁,所以互斥,子线程调用sub方法时,主线程不能调用main方法。但是还有一个问题,那就是有可能子线程一直抢占CPU资源,一直运行多次,或者运行完,主线程将拿到Business的对象锁,才开始运行,也有可能主线程一直抢占CPU资源,怎么解决这个问题了,那么就需要使用wait/notify了。 class Business{ // 子线程是否可以调用 private boolean subShould = true; // 子线程业务方法 public synchronized void sub(int i){ while(!subShould){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 10; j++) { System.out.println("sub thread sequence of " + j + " ,loop of " + i); } subShould = false; this.notify(); } // 主线程业务方法 public synchronized void main(int i){ while(subShould){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 100; j++) { System.out.println("main thread sequence of " + j + " ,loop of " + i); } subShould = true; this.notify(); } } > 使用一个变量来进行判断,首先,TraditionalThreadCommunication 类中main方法执行的时候,假设子线程先抢占到资源,拿到了Business类的对象锁,那么调用Business类中的sub方法,那么进行10次打印,打印完了之后,变量重新赋值,然后两个线程再次争夺CPU资源,假设还是子线程抢占到了CPU资源,拿到了Business的对象锁,那么进行sub方法的时候,由于subShould的值已经设置为false,那么会调用this.wait();即子线程释放Business类的对象锁,那么变成再次将占资源,如果这时候,主线程拿到了对象锁,那么执行打印,设置变量值为true,并唤醒释放对象锁之后处于等待状态的子线程,然后子线程开始调用,如此循环下去,打印结果就是要求所需要的。 -------------------- ## 打印一次A,两次B,三次C,如此依次循环打印50次。 ## package test01; public class ThreadPrintTest{ public static void main(String[] args) { final PrintChar print = new PrintChar(); // 线程A new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { print.printA(); } } }).start(); // 线程B new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { print.printB(); } } }).start(); // 线程C new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { print.printC(); } } }).start(); } } // 线程打印类 class PrintChar{ private int count = 0; // 打印一次A public synchronized void printA(){ while((count = count % 3) != 0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("A"); count++; this.notifyAll(); } // 打印两次B public synchronized void printB(){ while((count = count % 3) != 1){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 2; i++) { System.out.println("B"); } count++; this.notifyAll(); } // 打印三次C public synchronized void printC(){ while((count = count % 3) != 2){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 3; i++) { System.out.println("C"); } count++; this.notifyAll(); } } > 这个代码就不解释了,自己应该看的懂了。
相关 Java多线程编程:同步和互斥问题 在Java多线程编程中,同步和互斥问题是关键概念。这两个概念主要用来解决多个线程对共享资源的访问顺序以及防止数据不一致的问题。 1. **同步**: - 使用synch Bertha 。/ 2024年09月17日 12:27/ 0 赞/ 11 阅读
相关 并发编程难题:Java多线程同步与互斥案例 在Java多线程编程中,同步和互斥是两个重要的概念,用于保证共享资源的正确使用。 1. **同步(Synchronization)**: - **锁(Locks)**: 迈不过友情╰/ 2024年09月10日 13:24/ 0 赞/ 23 阅读
相关 【高并发基石】多线程、守护线程、线程安全、线程同步、互斥锁 很久之前就听过互联架构中有三高,高可用、高并发、高性能,多线程是处理高并发问题的基石,起步阶段一定要对线程有一个系统深刻的印象,为以后做准备 拼搏现实的明天。/ 2024年04月22日 19:15/ 0 赞/ 33 阅读
相关 多线程编程之四——线程的同步 多线程编程之四——线程的同步 作者:韩耀旭 [下载源代码][Link 1] 八、线程的同步 虽然多线程能给我们带来好处,但是也有不少问题需要解决。例如,对于 「爱情、让人受尽委屈。」/ 2022年09月19日 12:15/ 0 赞/ 66 阅读
相关 多线程并发编程(四):多线程同步互斥Wait/Notify 前言 > 前面说了使用Synchronized来进行线程之间的同步,接下来说明wait/notify的使用。 > > 首先wait/notify必须结合synchron 旧城等待,/ 2022年07月30日 15:27/ 0 赞/ 385 阅读
相关 多线程并发编程(三):多线程同步互斥Synchronized 前言 > 其实就是针对线程安全问题进行的,最经典的问题就是银行转账的问题,A向B转账的同时,A也在存钱,比如A有1000元,向B转200元,A自己存300元,按道理最后是 怼烎@/ 2022年07月30日 15:23/ 0 赞/ 239 阅读
相关 Linux多线程编程(二)-----同步与互斥 Linux多线程编程(一):[http://blog.csdn.net/llzk\_/article/details/55670172][http_blog.csdn.net_ àì夳堔傛蜴生んèń/ 2022年07月12日 08:54/ 0 赞/ 241 阅读
相关 多线程与并发----线程互斥技术 1、使用synchronized代码块 2、使用synchronized方法 3、分析静态方法锁使用的同步监视器对象是什么? 4、wait与notify实现线程间的通信 柔光的暖阳◎/ 2021年09月15日 09:42/ 0 赞/ 454 阅读
相关 Linux多线程——使用互斥量同步线程 前文再续,书接上一回,在上一篇文章: [Linux多线程——使用信号量同步线程][Linux]中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说 深碍√TFBOYSˉ_/ 2021年09月02日 08:33/ 0 赞/ 419 阅读
还没有评论,来说两句吧...