死锁和线程安全的问题
死锁
死锁是指多个线程运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
产生死锁的四个必要条
- 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
- 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
- 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
编写产生死锁的代码
public class TestDeadLock { private static Object obj1 = new Object(); private static Object obj2 = new Object(); public static void main(String[] args) { new Thread(new Thread1()).start(); new Thread(new Thread2()).start(); } private static class Thread1 implements Runnable { @Override public void run() { synchronized (obj1) { System.out.println("Thread1 拿到了 obj1 的锁!"); try { // 停顿2秒的意义在于,让Thread2线程拿到obj2的锁 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println("Thread1 拿到了 obj2 的锁!"); } } } } private static class Thread2 implements Runnable { @Override public void run() { synchronized (obj2) { System.out.println("Thread2 拿到了 obj2 的锁!"); try { // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1) { System.out.println("Thread2 拿到了 obj1 的锁!"); } } } } }
使用jstack加上进程号进行查看
预防死锁:
- 资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
- 只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
- 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
- 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
线程安全
模拟线程不安全代码
public class Test { private static Integer count=0; public static void add(){ count++; } static CountDownLatch downLatch=new CountDownLatch(100); public static void main(String[] args) throws InterruptedException { for (int i = 0; i <100 ; i++) { new Thread(()->{ for (int j = 0; j <10 ; j++) { add(); } downLatch.countDown(); }).start(); } downLatch.await(); System.out.println(count); } }
解决方案
1:添加synchronized
public class Test { private static Integer count=0; public synchronized static void add(){ count++; } static CountDownLatch downLatch=new CountDownLatch(100); public static void main(String[] args) throws InterruptedException { for (int i = 0; i <100 ; i++) { new Thread(()->{ for (int j = 0; j <10 ; j++) { add(); } downLatch.countDown(); }).start(); } downLatch.await(); System.out.println(count); } }
2.加lock锁
import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; public class Test { private static Integer count = 0; public static void add() { try { reentrantLock.lock(); count++; } catch (Exception e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } } static ReentrantLock reentrantLock = new ReentrantLock(); static CountDownLatch downLatch = new CountDownLatch(100); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 100; i++) { new Thread(() -> { for (int j = 0; j < 10; j++) { add(); } downLatch.countDown(); }).start(); } downLatch.await(); System.out.println(count); } }
3.上面的这种情况还可以使用原子类
package test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class Test { private static AtomicInteger count = new AtomicInteger(0); public static void add() { count.incrementAndGet(); } static CountDownLatch downLatch = new CountDownLatch(100); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 100; i++) { new Thread(() -> { for (int j = 0; j < 10; j++) { add(); } downLatch.countDown(); }).start(); } downLatch.await(); System.out.println(count); } }