博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java基础之死锁与线程通信
阅读量:6429 次
发布时间:2019-06-23

本文共 4161 字,大约阅读时间需要 13 分钟。

回顾

在上一篇 介绍了怎么处理线程安全问题,主要的思路就是使用线程同步的方法,线程A执行时获取 "锁",然后对共享数据进行操作,操作完毕后退出,线程B获取锁对共享数据进行操作。

我们看到此时此时线程B必须等线程A对共享数据操作完毕后才能对共享数据进行操作。此时虽然性能有所下降,但是保证了线程安全和数据准确性。我们在多线程编程要时刻注意线程安全的问题。且注意:

  • 操作共享数据代码块的判定
  • 锁对象的选择(锁需要是同一个)

死锁

我们先前都是讲的都是单个锁的问题,但是当锁有多个时,会不会出现什么其他问题呢? 例如

public class TestMain {    static StringBuffer sb1= new StringBuffer();    static StringBuffer sb2= new StringBuffer();    public static void main(String[] args) {        new Thread(){            public  void  run(){                synchronized (sb1){                    try {                        Thread.currentThread().sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    sb1.append("A");                    synchronized (sb2){                        sb2.append("B");                        System.out.println(sb1);                        System.out.println(sb2);                    }                }            }        }.start();        new Thread(){            public  void  run(){                synchronized (sb2){                    try {                        Thread.currentThread().sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    sb1.append("C");                    synchronized (sb1){                        sb2.append("D");                        System.out.println(sb1);                        System.out.println(sb2);                    }                }            }        }.start();    }}复制代码

我们创建了两个线程(匿名方式),且有两个锁,执行这段代码,你会发现控制台卡住了,如下图。

此时因为都在等待对方的锁,故称为死锁。

我们在编码实践中因尽量避免以上问题,在现实情况下多个锁不可避免时,我们可以设定锁的优先级别 例如A>B 任何线程都需要先获得A 才能获得B,此时避免死锁。更糟糕都情况下,我们可以kill一个线程使得目标锁释放,使得程序继续运行。

线程通信

在 讲解了多个关于Thread类的方法,回顾下

Thread.currentThread() //获取当前线程//以下都是线程实例上的方法setName(); //设置线程名字getName(); //获取线程名字yield();//显示释放cpu的执行权  join();//在一个线程执行中调用另一个目标线程的join方法,意味着立马执行目标线程,且执行完毕才回到原线程isAlive();//判断当前线程是否还存活sleep();//显示的让线程睡眠setPriority() //设置当前线程的优先级getPriority()//获取当前线程的优先级复制代码

此时我们补充三个Object类下关于线程的三个方法

wait();//令当前线程挂起放弃cpu且放弃同步资源notify();//唤醒正在排队等待同步资源的线程,其中优先级最高的结束等待notifyAll();//唤醒正在排队的等待资源的所有线程。复制代码

可见wait方法与notify notifyAll是对应的操作:那么我们可以利用这三个方法使得线程交替执行,即线程相互通信。

注:Object中在三个方法只能在synchronized方法或者同步代码块中执行,否则会抛异常

案例

使两个线程打印1到100 且两个线程交替打印 代码实现:(两个线程打印1到100 没有交替打印版本)

class PrintNum implements Runnable{   int num=1;   @Override   public void run() {       while (true){           synchronized (this) {               if(num<=100){                   try {                       Thread.currentThread().sleep(10);                   } catch (InterruptedException e) {                       e.printStackTrace();                   }                   System.out.println(Thread.currentThread().getName()+":"+num);                   num++;               }else {                   break;               }           }       }   }}PrintNum printNum=new PrintNum();Thread t1=new Thread(printNum);Thread t2=new Thread(printNum);t1.setName("A");t2.setName("B");t1.start();t2.start();复制代码

利用notify await 实现交替打印代码

class PrintNum implements Runnable{   int num=1;   @Override   public void run() {       while (true){           synchronized (this) {               notify();               if(num<=100){                   try {                       Thread.currentThread().sleep(10);                   } catch (InterruptedException e) {                       e.printStackTrace();                   }                   System.out.println(Thread.currentThread().getName()+":"+num);                   num++;               }else {                   break;               }               try {                   wait();               } catch (InterruptedException e) {                   e.printStackTrace();               }           }       }   }}复制代码

控制台打印如下

解释下:当第一个线程A进入时notify执行,但此时没有等待状的但线程,打印1后,await(),线程A进入等待状态且释放同步锁,线程B开始执行,进入synchronized代码块内,此时已经获取了锁,notify()后,唤醒了线程A,但是锁没释放,打印2后,await()进入等待状态,然后线程A获得锁资源,又进入同步代码块执行,依次下去边交替打印。仿佛两个线程之间实现了相互通信。

总结

本篇主要讲解了 死锁和线程通信两个概念

  • 什么是死锁问题,应该如何避免
  • 线程通信中Object类中notify notifyAll await的准确含义和使用,应特别区分sleep与await区别,其中sleep不释放锁资源,而await释放锁资源。

喜欢本文的朋友们,欢迎长按下图关注订阅号"我的编程笔记",收看更多精彩内容~~

转载地址:http://rziga.baihongyu.com/

你可能感兴趣的文章
[开源]KJFramework.Message 智能二进制消息框架 -- 新能力
查看>>
uni-app缓存器的封装
查看>>
ico 图标 生成 工具 网站
查看>>
Postman 网络调试工具
查看>>
Python教程6
查看>>
zabbix实现自动发现功能添加磁盘监控
查看>>
ext grid 前台grid加载数据碰到数据重复只显示一条
查看>>
Eclipse智能提示及快捷键
查看>>
在windows下安装php redis扩展
查看>>
PHP漏洞 (转)
查看>>
js获取iframe的id
查看>>
一个完整的WSDL文档及各标签详解
查看>>
mysql8.0.14 安装
查看>>
svn is already locked解决方案
查看>>
C++基础算法学习——猜假币
查看>>
1039. 到底买不买(20)
查看>>
JavaScript的块级作用域
查看>>
前端将markdown转换成html
查看>>
Hibernate 框架入门(一)
查看>>
缩略信息是: sending message to a Handler on a dead thread 我是用IntentService时报的
查看>>