目录
原因分析
解决线程安全问题——加锁
synchroized()
死锁
死锁的四个必要条件
线程安全是指多个线程同时访问共享资源时,不会产生不正确的结果。线程安全问题的主要原因是多个线程对共享数据进行读写操作时的并发性。这可能导致竞态条件(Race Condition)、数据不一致、死锁等问题。为了解决线程安全问题,可以采取以下几种方案:
原因分析
1.抢占式执行,随即调度
2.多个线程同时修改同一个变量,这里不一定非得是变量,硬盘上的数据,网络上的数据等等
3.修改操作数不是原子的
4...内存可见性
5.指令重排序
解决线程安全问题——加锁
synchroized()
synchroized带有锁对象,锁对象存在的意义,就只是起到“身份标识”的效果,两个针对同一个对象加锁,就可能产生阻塞/锁竞争/锁冲突
举例
public class Test {public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 10000; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 10000; i++) {count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}
上述代码中,两个线程针对同一个变量分别进行10000次的自增运算,但是输出的结果却没有到20000,这是典型的多个线程同时修改同一个变量产生的线程安全问题,在这种情况下,我们就需要加锁,代码如下
public class Test {public static int count = 0;public static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread t1 = new Thread(()->{synchronized (lock){for (int i = 0; i < 10000; i++) {count++;}}});Thread t2 = new Thread(()->{synchronized (lock){for (int i = 0; i < 10000; i++) {count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}}
上述代码中,通过加锁,将线程中的运算打包成原子的,当线程一没有执行完的时候,线程二就会进行阻塞等待
死锁
所谓的死锁就是线程一拥有锁1,线程二拥有锁2,双方在拥有自身锁的同时尝试获取对方的锁,最终两个线程就会进入无线等待的状态,这就是死锁。
举例
死锁的四个必要条件
1.锁具有互斥性。(基本特点,一个线程拿到锁之后,其他线程就需要阻塞等待)
2.锁不可抢占,一个线程拿到锁之后,除非他自己主动释放锁,否则别人抢不走
3.请求和保持,一个线程拿到一把锁之后,不释放这个锁的前提下,再次 尝试获取其他锁
4.循环等待 多个线程获取多个锁的过程中,出现了循环等待 A等待B,B又等待A 。
必要条件缺一不可,任何一个死锁的场景都必须同时具备上述四点,只要缺少一个,都不会构成死锁