多线程并发编程(四):多线程同步互斥Wait/Notify
2016-04-19 17:16
399 查看
前言
前面说了使用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设计模式心得
- BeautifulSoup_python3
- 关于JAVA中RSA加签解签,私钥加密公钥解密和公钥加密私钥解密代码详解
- MyEclipse从数据库反向生成实体类通过Hibernate的方式----mysql数据库实例
- java基本数据类型传递与引用传递区别详解
- Java 线程第三版 第四章 Thread Notification 读书笔记
- Struts2 Could not find action or result:错误
- JavaMail技术 用java代码发送邮件
- 使用springmvc后事物不起作用的原因
- 《C++primer》读书笔记三
- spring 不同注解的使用场景
- JAVA虚拟机之六:虚拟机性能监控和故障处理工具
- NYOJ 30 Gone Fishing(贪心)(个人理解笔记)
- JAVA虚拟机之五:常见配置与范例
- Maven搭建SSM(SpringMVC + Spring + Mybaits)开发环境 (下)
- 【干货】Spring MVC与JAX-RS比较与分析
- struts2开发7--在struts2中实现上传文件类型过滤
- JAVA虚拟机之四:G1垃圾收集器
- Eclipse 出现的问题总结
- 用python进行科学统计及数据挖掘--便捷工具环境搭建