Java使用同步解决线程安全问题的弊端及带来的问题
一、多线程使用同步解决方案引入的问题
由于多线程会引入线程安全问题,我们使用了同步或者加锁的方法来解决这个问题,但是使用同步的方法也会带来相应的弊端。
主要有如下两个弊端:
1、使用同步的方法效率低
2、如果出现同步嵌套,会出现死锁问题。
对于第一个问题,告诉我们要慎用同步,只在必要的位置添加同步方法,而不是为了安全添加不必要的同步,这样会导致效率的降低,因为如果使用了同步会有锁机制,执行相关代码前会对锁进行相应的检查,这样就会降低效率。第二个问题是主要讲解的问题,这个我们需要在程序中尽量避免,如果出现死锁问题,会导致所有的工作都无法正常完成,陷入相互等待的状态。
二、死锁案例
通过一段代码来直观的了解死锁发生的场景。代码如下:
package com.example.thread.one;
public class DieLockDemo {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
MyThread myThread1 = new MyThread(true, lockA, lockB);
MyThread myThread2 = new MyThread(false, lockA, lockB);
myThread1.setName("线程1");
myThread2.setName("线程2");
myThread1.start();
myThread2.start();
}
}
class MyThread extends Thread{
private boolean flag;
private Object lockA;
private Object lockB;
public MyThread(boolean flag, Object lockA, Object lockB) {
super();
this.flag = flag;
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
super.run();
if(flag){
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " :if lockA");
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " :if lockB");
}
}
}else{
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " :else lockB");
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " :else lockA");
}
}
}
}
}
上边程序可能出现的运行结果如下:
分析出现的原因:
当线程1获取的lockA的锁后执行完System.out.println(Thread.currentThread().getName() + " :if lockA");语句后时间片用完,线程2获取到时间片并且进入运行状态,线程2获取到lockB的锁后执行System.out.println(Thread.currentThread().getName() + " :else lockB");后需要获取lockA的锁,发现线程1还没有释放lockA的锁,因此进入阻塞状态,接下来线程1进入运行状态,同样的,线程1接着执行发现需要lockB的锁,但是这个锁被线程2使用也没有释放,此时线程1也进入阻塞状态。这样就产生了所谓的死锁问题,线程1和线程2相互等待,都无法执行接下来的代码。