9. 多线程 Part 3 同步与死锁 --- 学习笔记
来源:互联网 发布:淘宝的伞黑胶都在外面 编辑:程序博客网 时间:2024/06/02 05:31
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(); }}
程序运行结果:
------------------------------------------------- 张三对李四说:“你给我画,我就把书给你。”李四对张三说:“你给我书,我就把画给你。”
[以下代码不再执行,程序进入死锁状态]
从程序的运行结果中可以发现,两个线程都在彼此等着对方的执行完成,这样,程序就无法向下继续执行,从而造成了死锁的现象。
- 关于同步与死锁, 多个线程共享同一资源时需要进行同步,以保证资源操作的完整性;但是过多的同步就有可能产生死锁。。。
关于线程的死锁、互锁、互斥锁, 以及它们产生的原因、排错方法等等要自己查资料看看!!!!!!!!!!!
- 9. 多线程 Part 3 同步与死锁 --- 学习笔记
- 多线程 同步与死锁
- Java 线程同步与死锁 学习笔记
- java 多线程学习笔记2-同步代码块,死锁
- Java 多线程同步与死锁
- 多线程之同步与死锁
- JAVA - 多线程 - 同步与死锁
- 9. 多线程 Part 1 --- 学习笔记
- 9. 多线程 Part 2 --- 学习笔记
- 学习笔记——同步、通信与死锁(1)
- 学习笔记——同步、通信与死锁(2)
- OS学习笔记——进程同步与死锁1
- 关于Java多线程---------(3,同步与死锁)
- Java多线程之同步与死锁
- Java多线程之同步与死锁
- 多线程同步与死锁深入分析
- Java多线程的同步与死锁
- Java多线程 线程同步与死锁
- 确定进制(经典水题)
- 垃圾回收(garbage collection)介绍
- 直接路由的高可用LVS集群配置
- 程序员必须知道的10大基础实用算法及其讲解
- OpenCV的序列数据结构
- 9. 多线程 Part 3 同步与死锁 --- 学习笔记
- 动态链接库和静态链接库
- Struts2教程5:使用Validation框架验证数据
- TEST
- java利用Base64编码和解码图片文件
- OpenSSL(openssl-1.0.1h)编译与安装(Win7)
- CSS 的优先级机制
- Core Animation 的几何变换
- 收藏一个开源.netCMS项目