9. 多线程 Part 3 同步与死锁 --- 学习笔记
2014-07-02 16:55
465 查看
9.6 同步与死锁
一个多线程的程序如果是通过Runnabl接口实现的,则意味着类中的属性将被多个线程共享,那么这样一来就会造成一种问题,如果这多个线程操作同一资源时就有可能出现资源的同步问题。例如, 卖票程序,如果多个线程同时操作时就有可能出现卖出票为负数的问题。
9.6.1 问题的产生
范例:通过Runnable接口实现多线程,并产生3个线程对象,同时卖5张票。
运行结果截图
从程序的运行结果中可以发现,程序中加入了延迟操作,所以在运行的最后出现了负数的情况,那么为什么现在会产生这样的问题呢?
上面程序对于票数的操作步骤如下:
判断票数是否大于0,大于0则表示还有票可以卖
如果票数大于0,则将票卖出。
但是,代码中,在步骤1和步骤2之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减法操作之前,其他线程就已经将票数减少了,这样一来就会出现票数为负的情况。
![](http://img.blog.csdn.net/20140703105214859)
如果想解决这样的问题,就必须使用同步。
所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行,如下图所示:
![](http://img.blog.csdn.net/20140703105238796)
![](http://img.blog.csdn.net/20140703105352656)
9.6.2 使用同步解决问题
解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成。
同步代码块
所谓的代码块就是指使用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块前面加上synchronized关键字,则此代码块就称为同步代码块。 其格式如下
*******从上面的格式可以看出,在使用同步代码块时必须制定一个同步对象,但一般都将当前对象(this)设置成同步对象!!!*******
范例:使用同步代码块解决的同步问题
运行结果:
将上面代码的取值和修改值的操作代码进行了同步,所以不会再出现卖出票为负数的情况了。
同步方法
除了可以将需要的代码设置成同步代码块外,也可以使用synchronized关键字将一个方法声明成同步方法。其格式如下:
范例: 使用同步方法解决卖票出负数的情况
运行结果如下,与上面同步代码块实现了完全相同的功能。
方法定义的完整格式
9.6.3 死锁
同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如, 现在张三想要李四的画,李四想要张三的书;张三对李四说“把你的画给我,我就给你书”,李四也对张三说了:“把你的书给我,我就给你画”,此时,张三在等着李四的答复,而李四也在等着张三的答复;那么这样下去最终结果就是,张三得不到李四的画,李四也得不到张三的书。这就是死锁的概念!!
所谓死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。
范例:死锁
程序运行结果:
[以下代码不再执行,程序进入死锁状态]
从程序的运行结果中可以发现,两个线程都在彼此等着对方的执行完成,这样,程序就无法向下继续执行,从而造成了死锁的现象。
关于同步与死锁, 多个线程共享同一资源时需要进行同步,以保证资源操作的完整性;但是过多的同步就有可能产生死锁。。。
关于线程的死锁、互锁、互斥锁, 以及它们产生的原因、排错方法等等要自己查资料看看!!!!!!!!!!!
9.6 同步与死锁
一个多线程的程序如果是通过Runnabl接口实现的,则意味着类中的属性将被多个线程共享,那么这样一来就会造成一种问题,如果这多个线程操作同一资源时就有可能出现资源的同步问题。例如, 卖票程序,如果多个线程同时操作时就有可能出现卖出票为负数的问题。
9.6.1 问题的产生
范例:通过Runnable接口实现多线程,并产生3个线程对象,同时卖5张票。
class MyThread implements Runnable{ private int ticket = 5; public void run(){ for (int i = 0; i < 7; i++){ if (ticket > 0){ try{ Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("卖票: ticket = " + ticket--); } } } } public class SyncDemo01{ public static void main(String args[]){ MyThread my = new MyThread(); new Thread(my, "Thread-A").start(); new Thread(my, "Thread-B").start(); new Thread(my, "Thread-C").start(); } }
运行结果截图
卖票: ticket = 2 卖票: ticket = 1 卖票: ticket = 0 <span style="color:#ff0000;">卖票: ticket = -1</span> -------------------------------------------------
从程序的运行结果中可以发现,程序中加入了延迟操作,所以在运行的最后出现了负数的情况,那么为什么现在会产生这样的问题呢?
上面程序对于票数的操作步骤如下:
判断票数是否大于0,大于0则表示还有票可以卖
如果票数大于0,则将票卖出。
但是,代码中,在步骤1和步骤2之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减法操作之前,其他线程就已经将票数减少了,这样一来就会出现票数为负的情况。
如果想解决这样的问题,就必须使用同步。
所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行,如下图所示:
9.6.2 使用同步解决问题
解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成。
同步代码块
所谓的代码块就是指使用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块前面加上synchronized关键字,则此代码块就称为同步代码块。 其格式如下
synchronized(同步对象){ 需要同步的代码; }
*******从上面的格式可以看出,在使用同步代码块时必须制定一个同步对象,但一般都将当前对象(this)设置成同步对象!!!*******
范例:使用同步代码块解决的同步问题
class MyThread implements Runnable{ private int ticket = 5; public void run(){ for (int i = 0; i < 7; i++){ synchronized(this){ if (ticket > 0){ try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("卖票: ticket = " + ticket--); } } } } } public class SyncDemo01{ public static void main(String args[]){ MyThread my = new MyThread(); new Thread(my, "Thread-A").start(); new Thread(my, "Thread-B").start(); new Thread(my, "Thread-C").start(); } }
运行结果:
------------------------------------------------- 卖票: ticket = 5 卖票: ticket = 4 卖票: ticket = 3 卖票: ticket = 2 卖票: ticket = 1 -------------------------------------------------
将上面代码的取值和修改值的操作代码进行了同步,所以不会再出现卖出票为负数的情况了。
同步方法
除了可以将需要的代码设置成同步代码块外,也可以使用synchronized关键字将一个方法声明成同步方法。其格式如下:
synchronized 方法返回值 方法名称(参数列表){ 方法体; }
范例: 使用同步方法解决卖票出负数的情况
class MyThread implements Runnable{ private int ticket = 5; public void run(){ for (int i = 0; i < 7; i++){ this.sale(); } } private synchronized void sale(){ if (ticket > 0){ try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("卖票:ticket = " + ticket--); } } } public class SyncDemo02{ public static void main(String args[]){ MyThread my = new MyThread(); new Thread(my, "Thread-A").start(); new Thread(my, "Thread-B").start(); new Thread(my, "Thread-C").start(); } }
运行结果如下,与上面同步代码块实现了完全相同的功能。
------------------------------------------------- 卖票:ticket = 5 卖票:ticket = 4 卖票:ticket = 3 卖票:ticket = 2 卖票:ticket = 1 -------------------------------------------------
方法定义的完整格式
访问权限 {public|default|protected|private} [final] [static] [synchronized] 返回值类型|void 方法名称(参数类型 参数名称,…) [throws Exception1, Exception2] { [return [返回值|返回调用处]] }
9.6.3 死锁
同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如, 现在张三想要李四的画,李四想要张三的书;张三对李四说“把你的画给我,我就给你书”,李四也对张三说了:“把你的书给我,我就给你画”,此时,张三在等着李四的答复,而李四也在等着张三的答复;那么这样下去最终结果就是,张三得不到李四的画,李四也得不到张三的书。这就是死锁的概念!!
所谓死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。
范例:死锁
class Zhangsan{ public void say(){ System.out.println(" 张三对李四说:“你给我画,我就把书给你。”"); } public void get(){ System.out.println("张三得到画了!!"); } } class Lisi{ public void say(){ System.out.println("李四对张三说:“你给我书,我就把画给你。”"); } public void get(){ System.out.println("李四得到书了!!"); } }
public class ThreadDeadLock implements Runnable{ private static Zhangsan zs = new Zhangsan(); private static Lisi ls = new Lisi(); private boolean flag = false; //标记,用于判断哪个对象先执行 public void run(){ if(flag){ synchronized (zs){ zs.say(); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } synchronized(ls){ zs.get(); } } }else{ synchronized(ls){ ls.say(); try{ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } synchronized(zs){ ls.get(); } } } } public static void main(String args[]){ ThreadDeadLock t1 = new ThreadDeadLock(); ThreadDeadLock t2 = new ThreadDeadLock(); t1.flag = true; t2.flag = false; new Thread(t1, "Thread-A").start(); new Thread(t2, "Thread-B").start(); } }
程序运行结果:
------------------------------------------------- 张三对李四说:“你给我画,我就把书给你。” 李四对张三说:“你给我书,我就把画给你。”
[以下代码不再执行,程序进入死锁状态]
从程序的运行结果中可以发现,两个线程都在彼此等着对方的执行完成,这样,程序就无法向下继续执行,从而造成了死锁的现象。
关于同步与死锁, 多个线程共享同一资源时需要进行同步,以保证资源操作的完整性;但是过多的同步就有可能产生死锁。。。
关于线程的死锁、互锁、互斥锁, 以及它们产生的原因、排错方法等等要自己查资料看看!!!!!!!!!!!
相关文章推荐
- [原]Java多线程编程学习笔记之八:死锁(含代码)
- JAVA学习笔记--4.多线程编程 part4.JAVA多线程的高级类库
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
- Java多线程学习笔记—从Map开始说说同步和并发
- Java学习笔记—多线程(同步容器和并发容器)
- Java笔记3 多线程<1>线程概述、多线程的创建、多线程的安全问题、静态同步函数的锁、死锁
- java 多线程学习笔记2-同步代码块,死锁
- Android(java)学习笔记70:同步中的死锁问题以及线程通信问题
- [原]Java多线程编程学习笔记之八:死锁(含代码)
- Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解
- 9. 多线程 Part 1 --- 学习笔记
- 学习笔记——同步、通信与死锁(1)
- 9. 多线程 Part 2 --- 学习笔记
- .net多线程学习笔记 3 线程间同步
- Objective-C 学习笔记 - part 12 - 多线程
- Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解
- Java学习笔记79. 多线程的同步机制 -- synchronized 关键字
- .net多线程学习笔记 3 线程间同步
- Java学习提要——简述多线程的同步synchronized与死锁
- JAVA笔记14__多线程共享数据(同步)/ 线程死锁 / 生产者与消费者应用案例 / 线程池