菜鸡的Java笔记 - java 线程的同步与死锁 (同步 synchronization,死锁 deadlock)
线程的同步与死锁 (同步 synchronization,死锁 deadlock)
多线程的操作方法
1.线程同步的产生与解决
2.死锁的问题
同步问题的的引出
如果要想进行同步的操作,那么很明显就是多个线程需要访问同一资源
范例:以卖票程序为例
package cn.mysterious.study3; class MyThread implements Runnable{ private int ticket = 5; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + ",ticket = "+this.ticket --); } } } } public class StudyThread { public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); Thread t1 = new Thread(mt,"票贩子A"); Thread t2 = new Thread(mt,"票贩子B"); Thread t3 = new Thread(mt,"票贩子C"); t1.start(); t2.start(); t3.start(); } }
于是下面开始挖掘本程序所存在的问题
范例:观察程序的问题
package cn.mysterious.study3; class MyThread implements Runnable{ private int ticket = 5; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++) { if (this.ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",ticket = "+this.ticket --); } } } } public class StudyThread { public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); Thread t1 = new Thread(mt,"票贩子A"); Thread t2 = new Thread(mt,"票贩子B"); Thread t3 = new Thread(mt,"票贩子C"); t1.start(); t2.start(); t3.start(); } } // 结果出现了负数
bane这样的操作就属于线程的不同步的操作,所以发现多个线程操作时必须要考虑到资源同步的问题
实现同步操作
整个代码之中发现有一个逻辑上的流程错误了,以上的程序中,将判断是否有票,延迟,卖票分为了三个部分
那么实际上每个线程如果要执行卖票的话,其他线程应该等待当前线程执行完毕后才可以进入
如果要想在若干个代码上实现锁这个概念,那么就需要通过使用同步代码块或者同步方法来解决
1.同步代码块
使用 synchronized 关键字定义的代码块就称为同步代码块,但是在进行同步的时候需要设置有一个同步对象,那么往往可以使用 this 同步当前对象
package cn.mysterious.study3; class MyThread implements Runnable{ private int ticket = 500; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 1000; i++) { synchronized (this) { if (this.ticket > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",ticket = "+this.ticket --); } } } } } public class StudyThread { public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); Thread t1 = new Thread(mt,"票贩子A"); Thread t2 = new Thread(mt,"票贩子B"); Thread t3 = new Thread(mt,"票贩子C"); t1.start(); t2.start(); t3.start(); } }
加入同步之后整个代码执行的速度已经变慢了,而已不像美誉同步的时候那样多个线程一起进入到方法之中
异步的执行速度要快于同步的执行速度,但是异步的操作属于非线程安全的操作,而同步的操作属于线程的安全操作
2.同步方法
但是对于同步操作,除了用于代码块定义之外,也可以在方法上定义同步操作
package cn.mysterious.study3; class MyThread implements Runnable{ private int ticket = 500; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 1000; i++) { sale(); } } public synchronized void sale(){ // 卖票 if (this.ticket > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",ticket = "+this.ticket --); } } } public class StudyThread { public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); Thread t1 = new Thread(mt,"票贩子A"); Thread t2 = new Thread(mt,"票贩子B"); Thread t3 = new Thread(mt,"票贩子C"); t1.start(); t2.start(); t3.start(); } }
在多个线程访问同一资源时一定要考虑到数据的同步问题,同步就使用 synchronized 关键字
死锁分析(了解)
很明显,死锁是一种不确定的状态,对于死锁的操作应该出现的越少越好,下面的代码只是一个死锁的演示,代码不做任何实际的意义
package cn.mysterious.study3; class Kidnapper{ // 绑匪 public synchronized void say(Landlord dz){ System.out.println(" 绑匪说:给我3亿美金,放了你儿子!"); dz.get(); } public synchronized void get(){ System.out.println("绑匪得到钱"); } } class Landlord{ // 地主 public synchronized void say(Kidnapper bf){ System.out.println(" 地主说:放了我儿子,再给钱!"); bf.get(); } public synchronized void get(){ System.out.println("救回....."); } } public class StudyThread implements Runnable{ private Kidnapper bf = new Kidnapper(); private Landlord dz = new Landlord(); public StudyThread (){ new Thread(this).start(); dz.say(bf); } public static void main(String[] args) throws Exception { new StudyThread(); } @Override public void run() { // TODO Auto-generated method stub bf.say(dz); } }
面试题:请问多个线程访问同一资源时可能带来什么问题?以及会产生什么样的附加问题?
多个线程访同一资源时必须考虑同步问题,可以使用 synchronized 定义同步代码块或者是同步方法
程序中如果出现过多的同步那么就将产生死锁
总结
如果看见 synchronized 声明的方法,一定有爱记住这是一个同步方法,属于线程安全的操作