【Java编程由浅入深】线程互斥、同步与死锁——synchronized(this)&synchronized(非this)
目录
线程互斥、同步与死锁——synchronized(this)&synchronized(非this)
线程互斥、同步与死锁——synchronized(this)&synchronized(非this)
1.互斥
synchronized关键字在修饰方法时保证任何时刻只有一个线程在运行此方法。
但存在一个问题。如果现在有两个线程:一个plus,一个minus.
两个线程同时执行run中的opr方法,而该方法中有两个并列的if语句嵌套的无限循环(休眠语句便于最后输出结果的展示,否则输出太快看不到):
运行程序:(因为在有参构造myCalc对象的同时,对两个线程进行了实例化,在这里就可以直接调用线程的方法)
结果:只有plus线程访问到了该方法,因为我们设置了方法的互斥访问,同时刻只有一个线程在访问,但是因为其中有无限循环语句,则此线程永远不会退出访问。所以另一线程一直在等待。
这时出现了问题。我们要想用两个线程交替访问一个方法,而不出现此情况该怎么实现呢?运用到线程同步的理论。
2.线程同步与synchronized(this)语句
同样的在Runnable接口的实现类中声明两个线程(如果直接在Thread子类中声明线程和重修run方法,则只能实现多线程执行多个独立方法,而无法实现多线程并行执行同一个方法),
对run进行重写:
线程printA访问第一个if语句块,线程printB访问第二个if语句块。
同步的实现首先要使用:synchronized(this){ },作用:当对象访问到这个语句时,锁定这个对象访问的这块代码,访问代码期间,只能该对象访问,其他对象不能访问。
注意:线程也是一个对象。
程序运行:
由此可知程序的运行流程大概是:
java程序是顺序执行的:
线程printA运行到第一个if语句块——>成功(锁定)——> 唤醒 ———>执行——>阻塞让出————————>阻塞——>阻塞—————>阻塞——>阻塞(等唤醒)
线程printB运行到第一个if语句块——>等待(排斥)——>等待————>等待——————>访问第一个if语句块失败——>访问第二个if语句块———>成功(锁定)
两个线程交替执行方法
结果:
3.死锁与synchronized(非this)语句
死锁是程序无法执行却无逻辑错误的一种情况
当两个线程分别运行两个独立的方法是不会出现死锁现象的(各运行各的),那么我们怎么能让两个线程互相牵制来模拟出死锁的现象呢?
首先我们声明两个静态的成员对象并实例化,或者说两个引用的实例化。(Object类是所有类的父类)。
用这两个对象来牵制两条线程。这两个对象像两把锁,而两个独立的方法像两扇门。对应的锁打开对应的门。
来看两个方法:
threada线程访问PrintA方法,threadb访问PrintB方法。
假设threada线程先执行。
threada线程执行到synchronized(MyLock.lock1){ }时,类中的静态成员lock1被锁定了,因为静态成员只能实例化一次,所以相当于成员(钥匙)是固定唯一的(对于静态成员的所有的操作都是基于类的操作,相对于直接对模板进行操作);但这个钥匙被threada线程拿走了(锁定了)。这时threadb线程执行到synchronized(MyLock.lock2){ }时,类中的静态成员lock2被锁定了,这时两把钥匙都被拿走了。而当threada线程想离开synchronized(MyLock.lock1){ }代码块然后进入下面的代码块时,必须要有lock2这把钥匙,当threadb线程想离开synchronized(MyLock.lock2){ }代码块然后进入下面的代码块时,必须要有lock1这把钥匙。他们通过类访问两个对象时,发现都被锁定了,但他们只有拿到新钥匙才能离开,而自己需要的钥匙却在对方的手中。此时,程序出现了死锁。