Java面向对象进阶之多线程安全与唤醒机制
上一期,我们学习了多线程的概念以及多线程的基本使用,本章对多线程的剩余知识点,线程安全与解决,锁机制进行讲解,学完这些知识点,多线程基本上就结束了,下面开始今天的内容
T团队销售情况(使用Thred进行空调售卖)
public class Demo3 { public static void main(String[] args) { // T团队的老李来上班了... Thread t1 = new CompanyT("老李"); // T团队的小张张来上班了... Thread t2 = new CompanyT("小张张"); // T团队的老李出去售卖空调了. t1.start(); // T团队的小张张出去售卖空调了 t2.start(); } } // T团队 class CompanyT extends Thread{ // 当日的空调库存单 private int airNum = 25; // 销售团队人员名称 private String name; // 构造器 public CompanyT(String name) { this.name = name; } @Override public void run() { // 当天的销售情况 while (airNum>0) System.out.println("T团队"+name+"卖出一台空调"+", 当天空调剩余:"+ --airNum + "台"); } }
R团队销售空调情况(使用Runnable实现类)
package com.test01Thread; public class Demo4 { public static void main(String[] args) { // Runnable实例化 Runnable r = new CompanyR(); // R团队的小白白来上班了... Thread groupR1 = new Thread(r,"小白白"); // R团队的小倩来上班了... Thread groupR2 = new Thread(r,"小倩"); // R团队的小白白出去售卖空调了. groupR1.start(); // R团队的小倩出去售卖空调了 groupR2.start(); } } class CompanyR implements Runnable{ // 当日的空调库存单 private int airNum = 25; @Override public void run() { // 当天的销售情况 while (airNum>0) System.out.println("R团队"+Thread.currentThread().getName()+"卖出一台空调"+", 当天空调剩余:"+ --airNum + "台"); } }
当天任务结束后,Main老板为庆祝两个销售团队出色地完成任务,于是请大家吃火锅;并请大家分享自己地销售经验
听完两个团队地讲述和看到团队成员地销售单不免惊奇:为什么T团队会卖出50台空调?明明当天的任务是每个团队25台,于是打算为T团队加薪
R团队成员不干了,嚷嚷道:T团队作弊,他们复制了空调的销售单,库存就那么多,怎么可能超额完成,Main老板问道:你怎么知道?R团队中的小倩说,她在今日的销售单时,跟小张张的库存单剩余都是23台,当时以为只是拿错了,没注意。造成这个问题的原因是什么呢?
总结一下造成线程安全问题出现的原因:
经过上次差点闹出的事故,让工人紧急加班后,Main老板正在思索解决方案,这时有一位自称synchronized的年轻人来应聘,他一下就指出了之前的问题原因:
java中常使用关键字synchronized 来实现同步机制:
同步方法
public synchronized void method(){ 可能会产生线程安全问题的代码 }
同步代码块
synchronized(同步锁){ 需要同步操作的代码 }
Main老板决定试用synchronized一天,试试他的想法是否可行:
同步锁对象:
同步代码块的锁对象
// 非静态代码块中使用synchronized package com.test01Thread; /* 因为CompanyT 是Thread的子类,实例化的时候,就相当于重新开辟了一个内存空间,因此synchronized中的同步锁只能使用当前类的Class对象,如果使用This,那么它就相当于每个实例化对象都有一把锁,依旧是多条语句操作共享数据 * */ public class Demo3 { public static void main(String[] args) { // T团队的老李来上班了... Thread t1 = new CompanyT("老李"); // T团队的小张张来上班了... Thread t2 = new CompanyT("小张张"); // T团队的老李出去售卖空调了. t1.start(); // T团队的小张张出去售卖空调了 t2.start(); } } // T团队 class CompanyT extends Thread{ // 当日的空调库存 private static int airNum = 25; // 销售团队人员名称 private String name; // 构造器 public CompanyT(String name) { this.name = name; } @Override public void run() { // 当天的销售情况 while (true) { // 在非静态代码块中添加同步锁,此时因为是Thread的继承子类,所以这里同步锁必须是类的Class synchronized (CompanyT.class) { if (airNum>0) System.out.println("T团队" + name + "卖出一台空调" + ", 当天空调剩余:" + --airNum + "台"); else break; } } } }
同步方法的锁对象
// 非静态方法中使用同步锁 package com.test01Thread; /* M公司为了应对夏季空调销售旺季的需求,于是要求每天至少制作10台空调 现在需要交给手下的销售团队T和R进行销售 * */ public class Demo3 { public static void main(String[] args) { // T团队的老李来上班了... Thread t1 = new CompanyT("老李"); // T团队的小张张来上班了... Thread t2 = new CompanyT("小张张"); // T团队的老李出去售卖空调了. t1.start(); // T团队的小张张出去售卖空调了 t2.start(); } } // T团队 class CompanyT extends Thread{ // 当日的空调库存 private static int airNum = 25; // 销售团队人员名称 private String name; // 构造器 public CompanyT(String name) { this.name = name; } @Override public void run() { // 使用 saleAir(); } // 在非静态方法中使用同步锁 public synchronized void saleAir(){ // 当天的销售情况 while (true) { // 在非静态代码块中添加同步锁 if (airNum>0) System.out.println("T团队" + name + "卖出一台空调" + ", 当天空调剩余:" + --airNum + "台"); else break; } } }
经过一天的试用,果然没有再发生重复的库存问题,Main老板决定让synchronized 转正,并涨薪。T和R团队的小伙伴又佩服又羡慕,不禁感叹:知识就是金钱,这句话果真没错,看来以后要多学习
public class Singleton { private static Singleton ourInstance; public static Singleton getInstance() { //一旦创建了对象,之后再次获取对象,都不会再进入同步代码块,提升效率 if (ourInstance == null) { //同步锁,锁住判断语句与创建对象并赋值的语句 synchronized (Singleton.class) { if (ourInstance == null) { ourInstance = new Singleton(); } } } return ourInstance; } private Singleton() { } } //测试类 public class Demo { public static void main(String[] args) { //开启多个线程获取单例 new SingletonThread().start(); new SingletonThread().start(); new SingletonThread().start(); } } //线程类 class SingletonThread extends Thread{ @Override public void run() { Singleton instance = Singleton.getInstance(); System.out.println(instance);//打印对象地址,查看每个线程获取的实例是否同一个 } }
使用等待与唤醒
wati()
线程不再活动,不再参与调度,进入到wait set中,也不会再去竞争锁了,它会等待别的线程执行一个特别动作—notify()唤醒它或者等待时间到之后,就会重新进入到调度队列中notify()
选取所通知对象的wait set中的一个线程释放notifyAll()
释放所通知对象的wait set中的全部线程调用wait和notify方法需要注意的细节
生产者与消费者问题中其实隐含了两个问题:
M空调公司一个工人和一个销售员问题
package com.test03WriterandCooker; /* * 消费者与生产者问题 * */ public class Demo { public static void main(String[] args) { // 公共资源 WorkRoom workRoom = new WorkRoom(); // 创建工人,并启动 new Thread(new Worker(workRoom),"工人").start(); // 创建销售,并启动 new Thread(new Sales(workRoom),"Main老板").start(); } } // 工人 class Worker implements Runnable{ private WorkRoom workroom; // 创建构造器 public Worker(WorkRoom workroom) { this.workroom = workroom; } @Override public void run() { while (true) { try { Thread.sleep(10);//休息一下,放大效果 } catch (InterruptedException e) { e.printStackTrace(); } // 不停地生产组装空调 workroom.putAir(); } } } // 销售 class Sales implements Runnable{ private WorkRoom workroom; // 创建构造器 public Sales(WorkRoom workroom) { this.workroom = workroom; } @Override public void run() { while (true) { try { Thread.sleep(30);//休息一下,放大效果 } catch (InterruptedException e) { e.printStackTrace(); } // 不停地销售空调方法 workroom.getAir(); } } } // 公共资源存取空调的工作间 class WorkRoom{ // 最大生产空调数 private int maxAir = 5; // 当前空调数 private int airNum; public synchronized void putAir(){ if (airNum>=maxAir) { try { this.wait(100);//工作台满后等待 System.out.println("工人休息一下...."); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("工人生产了一台空调,并存放到工作间中,当前空调库存总剩余: "+ ++airNum + "台"); this.notify();//随机唤醒在此监视器上等待的一个线程 } public synchronized void getAir(){ if (airNum<0){ try { this.wait(10);//工作室空调满后等待 System.out.println("Main老板休息一下...."); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Main老板销售了一台空调,并从工作间中获取,当前空调库存总剩余: "+ --airNum + "台"); this.notify();//随机唤醒一个等待的线程 } }
代码解释:
释放锁的操作
不会释放锁的操作
死锁
public class TestDeadLock { public static void main(String[] args) { Object g = new Object(); Object m = new Object(); Owner s = new Owner(g,m); Customer c = new Customer(g,m); new Thread(s).start(); new Thread(c).start(); } } class Owner implements Runnable{ private Object goods; private Object money; public Owner(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { synchronized (goods) { System.out.println("先给钱"); synchronized (money) { System.out.println("发货"); } } } } class Customer implements Runnable{ private Object goods; private Object money; public Customer(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { synchronized (money) { System.out.println("先发货"); synchronized (goods) { System.out.println("再给钱"); } } } }
最后,两人争持不下…顾客最后亲自过来取货了…