死锁
死锁是指多个线程运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
产生死锁的四个必要条
- 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
- 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
- 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
编写产生死锁的代码
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++) {