Java多线程之线程同步和死锁
来源:互联网 发布:华龙点金手机交易软件 编辑:程序博客网 时间:2024/05/18 00:09
Java多线程之线程同步和死锁
- 前言:大家在看此文章时若有时间请先看前面的俩篇文章:Java多线程之多线程概述和俩种创建方式与Java多线程之线程状态转换、控制线程和线程同步。这三篇Java多线程文章均为总结性文章,适合自己复习使用。
(一).线程同步
为什么要实现线程同步?当使用多个线程来访问同一个数据时,很容易“偶然”出现线程安全问题。我用通俗的语言来说明:我们知道Thread类实现了Runnable接口,新建的线程中每个线程都共享Thread类的成员变量(也就是共享资源)。那么很有可能当线程足够多时俩个线程同时取得成员变量,同时对成员变量进行操作,这时候数据紊乱,出现线程不安全现象。
【经典的银行取钱问题】:假如你的银行账户里面有2000块钱,有一天你去银行柜台取钱,取1500,正在你办理的时候,你老婆去了取款机,她也取钱,事先没商量好谁取,所以她也想取1500。如果两个人都取走了1500,合起来就3000了,银行咋办???
【个人理解】:所以我个人把线程同步理解为:多个线程同时访问同一共享资源,并且对共享资源进行操作,导致操作之后的资源混乱,不符合正确地流程所得结果。我们来看下面一个具体问题:
以买车票为例,不管多少售票点可以买火车票,同一列火车的票数一定,也就是说把每个售票点理解为线程的话,所有线程共享一份票数,票数就是线程共享资源。卖票就是线程对共享资源的操作。
package Thread1;class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void run(){ for(int i=0;i<100;i++){ if(ticket>0){ // 还有票 try{ Thread.sleep(300) ; // 加入延迟 }catch(InterruptedException e){ e.printStackTrace() ; } System.out.println("卖票:ticket = " + ticket-- ); } } }};public class demo1{ public static void main(String args[]){ MyThread mt = new MyThread() ; // 定义线程对象 Thread t1 = new Thread(mt) ; // 定义Thread对象 Thread t2 = new Thread(mt) ; // 定义Thread对象 Thread t3 = new Thread(mt) ; // 定义Thread对象 t1.start() ; t2.start() ; t3.start() ; }};
运行结果:
卖票:ticket = 5卖票:ticket = 3卖票:ticket = 4卖票:ticket = 2卖票:ticket = 1卖票:ticket = 0卖票:ticket = -1
此时结果发现卖出的票数成负数,程序代码出现问题。
- 如上,程序出现问题有可能是线程2取出数据,但是还没有把ticket减一的数据送回去之前,线程n也取出了这个数据执行了减一操作,这样这俩个数据都执行了俩次减一操作导致数据为负。
【问题解决】
1.要解决这种问题必须使用同步,所谓同步是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等这个线程完成后才能继续执行。
2.使用同步方法解决:要想解决共享资源的同步操作问题,需要使用同步代码块或是同步方法俩种方式来完成。
- 【同步代码块】,使用synchronized关键字声明的代码块叫做同步代码块。格式如下:
synchronized(同步对象){ 需要同步的代码 }
- 上面synchronized后括号里的同步对象也叫做同步监视器!代码含义是:线程在执行需要同步的代码之前需要获得对同步监视器的锁定。【注意】任何时刻只有一个线程能获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。同步的时候必须指明:同步的对象,一般情况下,会将当前对象作为同步对象使用this表示,或是将要共享的成员变量。
package Thread1;class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void run(){ for(int i=0;i<100;i++){ synchronized(this){ // 要对当前对象进行同步 if(ticket>0){ // 还有票 try{ Thread.sleep(300) ; // 加入延迟 }catch(InterruptedException e){ e.printStackTrace() ; } System.out.println("卖票:ticket = " + ticket-- ); } } } }};public class demo1{ public static void main(String args[]){ MyThread mt = new MyThread() ; // 定义线程对象 Thread t1 = new Thread(mt) ; // 定义Thread对象 Thread t2 = new Thread(mt) ; // 定义Thread对象 Thread t3 = new Thread(mt) ; // 定义Thread对象 t1.start() ; t2.start() ; t3.start() ; }};
- 运行结果:
卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1
从运行结果可以发现,程序加入了同步操作,所以不会产生负数的情况,但是程序的执行效率明显降低很多。
【同步方法】:使用了synchronized声明的方法为同步方法。同步方法定义格式:
synchronized 方法返回值类型 方法名称(参数列表){}
package Thread1;class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void run(){ for(int i=0;i<100;i++){ this.sale() ; // 调用同步方法 } } public synchronized void sale(){ // 声明同步方法 if(ticket>0){ // 还有票 try{ Thread.sleep(300) ; // 加入延迟 }catch(InterruptedException e){ e.printStackTrace() ; } System.out.println("卖票:ticket = " + ticket-- ); } }};public class demo1{ public static void main(String args[]){ MyThread mt = new MyThread() ; // 定义线程对象 Thread t1 = new Thread(mt) ; // 定义Thread对象 Thread t2 = new Thread(mt) ; // 定义Thread对象 Thread t3 = new Thread(mt) ; // 定义Thread对象 t1.start() ; t2.start() ; t3.start() ; }};
- 运行结果:
卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1
(二).死锁
- 当俩个线程在互相等待对方释放同步监视器时就会发生死锁,java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程编程应该采取措施避免死锁出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
资源共享时候需要进行同步操作,程序中过多的同步会产生死锁。
死锁一般情况下表示互相等待,线程之间互相需要对方的资源程序才能执行下去。
package Thread1;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 demo1 implements Runnable{ private static Zhangsan zs = new Zhangsan() ; // 实例化static型对象 private static Lisi ls = new Lisi() ; // 实例化static型对象 private boolean flag = false ; // 声明标志位,判断那个先说话 public void run(){ // 覆写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[]){ demo1 t1 = new demo1() ; // 控制张三 demo1 t2 = new demo1() ; // 控制李四 t1.flag = true ; t2.flag = false ; Thread thA = new Thread(t1) ; Thread thB = new Thread(t2) ; thA.start() ; thB.start() ; }};
张三对李四说:“你给我画,我就把书给你。”李四对张三说:“你给我书,我就把画给你”
- 此时双方说完话都在等待对方回应,此时处于停滞状态,都在互相等待着对方回答。
- Java多线程之线程同步和死锁
- 多线程之同步和死锁
- java线程同步之死锁
- java多线程-同步和死锁
- Java多线程 线程同步与死锁
- java多线程之 ---- 线程死锁
- java多线程之线程死锁
- Java多线程线程、同步代码块、同步函数、死锁
- Java多线程之同步与死锁
- Java多线程之同步与死锁
- [Java]多线程之同步及死锁
- java线程同步之死锁问题
- java多线程的同步和死锁
- 多线程之同步死锁
- 面试问题之线程同步和死锁
- java多线程同步死锁
- 线程同步之死锁
- 线程同步之死锁
- 负载均衡详解
- 跨浏览器事件Event对象代码
- java 反射之属性和方法
- C++虚函数表剖析
- 各种分类算法优缺点比较
- Java多线程之线程同步和死锁
- Python库numpy中的Broadcasting机制解析
- 哈夫曼编码 wustOJ
- sublime 常用插件包整理及下载包
- php关于序列化和反序列化的应用
- java学习日记_71:BigDecimal类
- DVWA-1.9全级别教程之Brute Force
- Linux学习-Linux命令
- uva1326 Jurassic Remains