设计一个案例: 多线程运行环境下,假设程序要访问一个共享变量,对共享变量计算的中间结果用打印机打印出来,而打印机一次只允许一个线程访问
分析: 显然,一个线程要获取共享变量的访问权(对被用来同步访问的共享变量那把锁加锁——lockA),此外还要获取对打印机的访问权(对被用来同步访问打印机的锁成功加锁——lockB)。
public class Demo1 { static Object lockA = new Object(); static Object lockB = new Object(); public static void main(String[] args) { ABThread abThread = new ABThread(); BAThread baThread = new BAThread(); abThread.start(); baThread.start(); } } // 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机 class ABThread extends Thread { @Override public void run() { // 首先,尝试访问共享变量 synchronized (Demo1.lockA) { try { Thread.sleep(2000); System.out.println("ABThread 加 lockA锁"); } catch (InterruptedException e) { e.printStackTrace(); } // 访问共享变量做计算,得到一些结果 synchronized (Demo1.lockB) { // 把共享变量计算的结果发送到打印机来打印 System.out.println("ABThread 加 lockB锁"); } } } } // 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印 class BAThread extends Thread { @Override public void run() { // 首先,尝试访问打印机 synchronized (Demo1.lockB) { // 获取打印机的访问权 try { Thread.sleep(2000); System.out.println("BAThread 加 lockB锁"); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Demo1.lockA) { //访问共享变量, 计算出结果,把结果发送到打印机打印 System.out.println("BAThread 加 lockA锁"); } } } }运行结果: 可以看出程序没有运行结束(发生了死锁现象)
死锁的问题: 死锁是指两个以上的线程在执行过程中,因为争夺资源而产生的一种相互等待的现象。
死锁问题的两种常见解决方式: 1.将多线程获取锁的顺序调整相同 2.再定义一把新的锁,利用这把锁(synchronized+该对象),实现将加多把锁的操作变成一个原子操作 (原子操作即一组不可分割的操作,要么全做,要么一点也不做)
解决方案一的代码实现:
public class Demo1 { static Object lockA = new Object(); static Object lockB = new Object(); public static void main(String[] args) { ABThread abThread = new ABThread(); BAThread baThread = new BAThread(); abThread.start(); baThread.start(); } } // 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机 class ABThread extends Thread { @Override public void run() { // 首先,尝试访问共享变量 synchronized (Demo1.lockA) { try { Thread.sleep(2000); System.out.println("ABThread 加 lockA锁"); } catch (InterruptedException e) { e.printStackTrace(); } // 访问共享变量做计算,得到一些结果 synchronized (Demo1.lockB) { // 把共享变量计算的结果发送到打印机来打印 System.out.println("ABThread 加 lockB锁"); } } } } // 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印 class BAThread extends Thread { @Override public void run() { // 首先,尝试访问打印机 synchronized (Demo1.lockA) { // 获取打印机的访问权 try { Thread.sleep(2000); System.out.println("BAThread 加 lockB锁"); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Demo1.lockB) { //访问共享变量, 计算出结果,把结果发送到打印机打印 System.out.println("BAThread 加 lockA锁"); } } } }运行结果: 解决方案二的代码实现:
public class Demo1 { static Object lockA = new Object(); static Object lockB = new Object(); static Object allLock=new Object(); public static void main(String[] args) { ABThread abThread = new ABThread(); BAThread baThread = new BAThread(); abThread.start(); baThread.start(); } } // 向访问共享变量,在访问打印机,将对共享变量计算的结果发送到打印机 class ABThread extends Thread { @Override public void run() { synchronized(Demo1.allLock){ // 首先,尝试访问共享变量 synchronized (Demo1.lockA) { try { Thread.sleep(100); System.out.println("ABThread 加 lockA锁"); } catch (InterruptedException e) { e.printStackTrace(); } // 访问共享变量做计算,得到一些结果 synchronized (Demo1.lockB) { // 把共享变量计算的结果发送到打印机来打印 System.out.println("ABThread 加 lockB锁"); } } } } } // 先获取打印机的访问权,在尝试访问共享变量,将共享变量计算结果发送到打印机打印 class BAThread extends Thread { public void run() { synchronized (Demo1.allLock) { // 首先,尝试访问打印机 synchronized (Demo1.lockB) { // 获取打印机的访问权 try { Thread.sleep(2000); System.out.println("BAThread 加 lockB锁"); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Demo1.lockA) { //访问共享变量, 计算出结果,把结果发送到打印机打印 System.out.println("BAThread 加 lockA锁"); } } } } }运行结果截图: