进程是并发程序执行过程中资源分配的基本单元,线程是程序运行与调度的基本单元。
Java 真的可以开启线程吗?
不能
查看源码 Thread
类的start()
方法
方法调了start0()
这个本地方法, 底层的是C++ ,Java 无法直接操作硬件
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
线程有几个状态
public enum State {
NEW,//新生
RUNNABLE,//运行
BLOCKED,//阻塞
WAITING,//等待
TIMED_WAITING,//超时等待
TERMINATED;//终止
}
package juc;
public class LockTest {
@Test
public void synTest() {
//并发:多个线程操作同一个资源类,把资源类丢入线程
SynTicket ticket = new SynTicket();
new Thread(() -> {
for (int i = 1; i < 40; i++) ticket.sale();
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) ticket.sale();
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 40; i++)
ticket.sale();
}, "C").start();
}
}
//资源类 OOP 只有属性和方法
class Ticket {
private int number = 30;
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票,还剩:" + number);
}
}
}
三个实现类
ReentrantLock
一个可重入互斥Lock具有与使用synchronized方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
/**
* 非公平锁
* 非公平锁在实现的时候多次强调随机抢占
*/
static final class NonfairSync extends Sync {
...
}
/**
* 公平锁
* 实现机理在于每次有线程来抢占锁的时候,都会检查一遍有没有等待队列,如果有,按队列依次执行
*/
static final class FairSync extends Sync {
...
}
public ReentrantLock() {
sync = new NonfairSync();//默认使用非公平锁
}
public ReentrantLock(boolean fair) {//传入true使用公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
...
}
ReentrantReadWriteLock.ReadLock
读锁
ReentrantReadWriteLock.WriteLock
写锁
public class LockTest {
@Test
public void lockTest() {
LockTicket ticket = new LockTicket();
new Thread(() -> {
for (int i = 1; i < 40; i++) ticket.sale();
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) ticket.sale();
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 40; i++)
ticket.sale();
}, "C").start();
}
}
/**
* Lock三步曲
* 1.new ReentrantLock();
* 2.lock.lock();
* 3.finally=>lock.unlock();
*/
class LockTicket {
private int number = 30;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();//加锁
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票,还剩:" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
}
package juc;
/**
* 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*
* @author Manaphy
* @date 2020-07-05
*/
public class ProducerConsumer {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
//判断等待 业务 通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
问题存在,A B C D 4 个线程! 虚假唤醒
用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后。
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
C=>1
A=>2
C=>3
B=>2
B=>1
B=>0
C=>1
A=>2
C=>3
B=>2
B=>1
B=>0
C=>1
A=>2
C=>3
D=>2
D=>1
D=>0
B=>-1
D=>-2
D=>-3
D=>-4
D=>-5
D=>-6
D=>-7
D=>-8
C=>-7
A=>-6
C=>-5
A=>-4
C=>-3
A=>-2
C=>-1
A=>0
解决方案
if 改为 while 判断
public class ProducerConsumer {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
//判断等待 业务 通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
public class ProducerConsumer {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
//判断等待 业务 通知
class Data {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我+1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ConditionExample {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.printC();
}
}, "C").start();
}
}
class Data2 {
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA() {
lock.lock();
try {
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
number = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
number = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
number = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
结果如下
A=>1
B=>2
C=>3
A=>1
B=>2
C=>3
A=>1
B=>2
C=>3
A=>1
B=>2
C=>3
A=>1
B=>2
C=>3
public class ListTest {
public static void main(String[] args) {
/*
* List list = new ArrayList<>();
* java.util.ConcurrentModificationException --> 并发修改异常
* 并发下 ArrayList 是不安全的
* 解决方案
* 1.List list = new Vector<>();
* 2.List list = Collections.synchronizedList(new ArrayList<>());
* 3.List list = new CopyOnWriteArrayList<>();
*/
/*
* CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
* 多个线程调用的时候 list 读取的时候,固定的,写入(覆盖)
* 在写入的时候避免覆盖,造成数据问题!
* 底层 add() 方法使用了 lock 锁
*/
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
CopyOnWriteArrayList类底层 add() 方法
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public class SetTest {
public static void main(String[] args) {
/*
* Set set = new HashSet<>();
* 还是会出 java.util.ConcurrentModificationException 异常
*
* 解决方案
* 1.Set set = Collections.synchronizedSet(new HashSet<>());
* 2.Set set = new CopyOnWriteArraySet<>();
*/
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
HashSet的底层
public HashSet() {
map = new HashMap<>();
}
//add()方法 set 本质就是 map key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//PRESENT是一个空Object 不变的值
private static final Object PRESENT = new Object();
public class MapTest {
public static void main(String[] args) {
/*
* HashMap map = new HashMap<>();
* 也会报 java.util.ConcurrentModificationException
*
* 解决方案
* 1.Map map = Collections.synchronizedMap(new HashMap<>());
* 2.Map map = new ConcurrentHashMap<>();
*/
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
类似于 Runnable,与Runnable的区别
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//Runnable的实现类-> Class FutureTask 构造方法-> FutureTask(Callable callable)
//new Thread().start(); 怎么启动Callable
MyThread thread = new MyThread();
//适配类
FutureTask<Integer> futureTask = new FutureTask<>(thread);
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();//两条线程只会打印一个 call()...
/*
* 获取Callable的返回结果
* 不过这个方法可能会产生阻塞,一般把他放到最后
* 或者使用异步通讯来处理
*/
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("call()...");
return 1024;
}
}
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
A CountDownLatch用给定的计数初始化
/**
* 线程减法计数器
*
* @author Manaphy
* @date 2020-07-07
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//例子 所有人出门后才能关门
CountDownLatch countDownLatch = new CountDownLatch(6);//设置6个人
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "号出门了");
countDownLatch.countDown();//数量-1
}, String.valueOf(i)).start();
}
//等待计数器归零,再往下执行
countDownLatch.await();
//线程执行速度不一样 有的快有的慢 部分业务需要等所有线程结束后才能再处理后面的逻辑 此时 就需要 countDownLatch
System.out.println("关门");
}
}
结果如下
1号出门了
3号出门了
4号出门了
2号出门了
5号出门了
6号出门了
关门
允许一组线程全部等待彼此达到共同屏障点的同步辅助
/**
* 线程加法计数器
*
* @author Manaphy
* @date 2020-07-07
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
//例子:集齐7颗龙珠召唤神龙
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7; i++) {
//lambda不能操作 i ,所以定义 final temp
final int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集第" + temp + "颗龙珠");
try {
cyclicBarrier.await();//等待集齐龙珠
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
结果如下
Thread-0收集第1颗龙珠
Thread-6收集第7颗龙珠
Thread-3收集第4颗龙珠
Thread-4收集第5颗龙珠
Thread-1收集第2颗龙珠
Thread-2收集第3颗龙珠
Thread-5收集第6颗龙珠
召唤神龙
一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。
同一时间只能有指定数量个得到线程
/**
* 信号量
* 同一时间只能有指定数量个得到线程
*
* @author Manaphy
* @date 2020-07-07
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//例子:抢车位
//线程数量: 停车位
Semaphore semaphore = new Semaphore(3);
//共有6辆车
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();//得到
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放
}
}).start();
}
}
}
结果如下
Thread-0抢到车位
Thread-2抢到车位
Thread-1抢到车位
Thread-1离开车位
Thread-0离开车位
Thread-4抢到车位
Thread-2离开车位
Thread-3抢到车位
Thread-5抢到车位
Thread-5离开车位
Thread-4离开车位
Thread-3离开车位
读可以被多个线程同时读
写的时候只能有一个线程去写
package juc;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写锁演示
*
* @author Manaphy
* @date 2020-07-10
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 写入
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> myCache.put(temp + "", temp + ""), String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> myCache.get(temp + ""), String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
// 存,写
private final Map<String, Object> map = new HashMap<>();
//存,写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
}
// 取,读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
}
}
结果如下
1写入1
4写入4 <--写入的时候被插队了
4写入OK
2写入2
2写入OK
3写入3
5写入5
1读取1
1写入OK
1读取OK
5写入OK
3写入OK
4读取4
4读取OK
3读取3
2读取2
3读取OK
2读取OK
5读取5
5读取OK
加入读写锁
package juc;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写锁演示
*
* @author Manaphy
* @date 2020-07-10
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
// 写入
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> myCache.put(temp + "", temp + ""), String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> myCache.get(temp + ""), String.valueOf(i)).start();
}
}
}
/**
* 加入读写锁
*/
class MyCacheLock {
private final Map<String, Object> map = new HashMap<>();
// 读写锁: 更加细粒度的控制
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存,写入的时候,只希望同时只有一个线程写
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
Object put = map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读,所有人都可以读!
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
效果如下
1写入1
1写入OK
3写入3
3写入OK
4写入4
4写入OK
2写入2
2写入OK
5写入5
5写入OK
1读取1
2读取2
3读取3
4读取4
4读取OK
1读取OK
5读取5
5读取OK
3读取OK
2读取OK
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池!
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer(,) |
移除 | remove | poll() | take() | poll(,) |
检测队首元素 | element | peek | - | - |
@Test
public void test1() {
// 队列的大小
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// IllegalStateException: Queue full 抛出异常!
// System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
// java.util.NoSuchElementException 抛出异常!
// System.out.println(blockingQueue.remove());
}
@Test
public void test2() {
// 队列的大小
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));// true
System.out.println(blockingQueue.offer("b"));// true
System.out.println(blockingQueue.offer("c"));// true
System.out.println(blockingQueue.offer("d"));// false 不抛异常
System.out.println(blockingQueue.poll());// a
System.out.println(blockingQueue.poll());// b
System.out.println(blockingQueue.poll());// c
System.out.println(blockingQueue.poll());// null 不抛异常
}
@Test
public void test03() throws InterruptedException {
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// blockingQueue.put("d");// 队列没有位置了,一直阻塞
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
// blockingQueue.take();// 没有这个元素,一直阻塞
}
@Test
public void test04() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
// blockingQueue.offer("d", 2, TimeUnit.SECONDS); // 等待超过2秒就退出
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
blockingQueue.poll(2, TimeUnit.SECONDS); // 等待超过2秒就退出
}
package juc;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列演示
* *和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
* *put了一个元素,必须从里面先take取出来,否则不能再put进去值!
*
* @author Manaphy
* @date 2020-07-11
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + " put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + " put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
}
}
结果如下
T1 put 1
T2=>1
T1 put 2
T2=>2
T1 put 3
T2=>3
线程池:三大方法、7大参数、4种拒绝策略
线程池的好处
降低资源的消耗
提高响应的速度
方便管理。
线程复用、可以控制最大并发数、管理线程
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Executors 工具类、3大方法
*
* @author Manaphy
* @date 2020-07-11
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
// ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 10; i++) {
// 使用线程池来创建
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
单线程运行结果
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
固定线程池大小的运行结果
pool-1-thread-1 ok
pool-1-thread-4 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-3 ok
pool-1-thread-4 ok
pool-1-thread-1 ok
pool-1-thread-5 ok
可扩展线程池运行结果
pool-1-thread-1 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-5 ok
pool-1-thread-4 ok
pool-1-thread-7 ok
pool-1-thread-6 ok
pool-1-thread-8 ok
pool-1-thread-9 ok
pool-1-thread-10 ok
源码分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 本质是ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂 创建线程的 一般不用东
RejectedExecutionHandler handler // 拒绝策略) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
查看阿里巴巴公约规范发现规约以上三种方式都不好用, 要手动创建线程池
手动创建线程池
public class ThreadPoolDemo {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
//ExecutorService threadPool3 = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱
/*
* 自定义线程池
* 最大承载量(队列满了) = Deque + maximumPoolSize
*
* 四种拒绝策略
* new ThreadPoolExecutor.AbortPolicy() // 队列满了抛出异常 RejectedExecutionException
* new ThreadPoolExecutor.CallerRunsPolicy() // 哪里来的去哪里
* new ThreadPoolExecutor.DiscardPolicy() // 队列满了不会抛出异常,会丢掉任务
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了尝试和最早的竞争,也不会抛出异常
*
* 最大线程(maximumPoolSize)如何设置
* 1. CPU 密集型 设置为当前电脑最大核心数 Runtime.getRuntime().availableProcessors();
* 2. IO 密集型 设置为最大任务的2倍
*/
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
for (int i = 0; i < 20; i++) {
// 使用线程池来创建
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
ForkJoin 在 JDK 1.7 , 并行执行任务!提高效率。在大数据量时使用
package juc;
import org.junit.Test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
/**
* 计算从1到10_0000_0000的和
*
* @author Manaphy
* @date 2020-07-11
*/
public class ForkJoinDemo {
/**
* 普通的方法
*/
@Test
public void sum1() {
long start = System.currentTimeMillis();
long sum = 0L;
for (int i = 0; i <= 10_0000_0000; i++) {
sum += i;
}
System.out.println(sum);
System.out.println(System.currentTimeMillis() - start);//277
}
/**
* 使用并行流(最快)
*/
@Test
public void sum2() {
long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
System.out.println(sum);
System.out.println(System.currentTimeMillis() - start);//161
}
/**
* 使用 ForkJoin 的方法
*/
@Test
public void sum3() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinSum task = new ForkJoinSum(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
long sum = submit.get();
System.out.println(sum);
System.out.println(System.currentTimeMillis() - start);// 188
}
public static class ForkJoinSum extends RecursiveTask<Long> {
private final long start;
private final long end;
public ForkJoinSum(long start, long end) {
this.start = start;
this.end = end;
}
// 计算方法
@Override
protected Long compute() {
// 临界值
long temp = 100000L;
if ((end - start) < temp) {
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else { // forkJoin 递归
long middle = (start + end) / 2; // 中间值
ForkJoinSum task1 = new ForkJoinSum(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
ForkJoinSum task2 = new ForkJoinSum(middle + 1, end);
task2.fork(); // 拆分任务,把任务压入线程队列
return task1.join() + task2.join();
}
}
}
}
对将来的某个事件的结果进行建模
public class CompletableFutureDemo {
@Test
public void voidTest() throws ExecutionException, InterruptedException {
// 没有返回值的 runAsync 异步调用
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignored) {
}
System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
});
System.out.println("程序执行中...");
completableFuture.get();
}
@Test
public void returnTest() throws ExecutionException, InterruptedException {
// 有返回值的 supplyAsync 异步调用
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
// int i = 1 / 0;
return 1024;
});
//和 ES6 的 Promise类似
Integer integer = completableFuture.whenComplete((t, u) -> {
System.out.println("t = " + t);// 程序正常执行:t = 1024 出异常:t = null
System.out.println("u = " + u);// 程序正常执行:u = null 出异常:u = ...(异常信息) / by zero
}).exceptionally((e) -> {
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
return 400;
}
).get();
System.out.println(integer);// 程序正常执行:1024 出异常:400
}
}
JMM即为JAVA 内存模型(java memory model)
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
JMM对这八种指令的使用,制定了如下规则:
Volatile 是 Java 虚拟机提供轻量级的同步机制
可以保持 可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
public class VolatileDemo { // main线程
//不加 volatile 程序就会死循环
//加 volatile 可以保证可见性
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> { // 新线程 对主内存的变化是不知道的
while (num == 0) {
}
}).start();
TimeUnit.SECONDS.sleep(1);
num = 1;
System.out.println(num);
}
}
public class VolatileDemo2 {
// volatile 不保证原子性
private volatile static int num = 0;
static void add() {
num++;
}
public static void main(String[] args) {
//理论上 num=20000
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "->" + num);// main->14667
}
}
如果不加 lock 和 synchronized ,怎么样保证原子性
public class VolatileDemo2 {
//不使用lock 和 synchronized 的解决方案
private static final AtomicInteger atomicInteger = new AtomicInteger();
static void add() {
atomicInteger.getAndIncrement();
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "->" + atomicInteger);// main->20000
}
}
指令重排:你写的程序,计算机并不是按照你写的那样去执行的
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x * x; // 4
我们所期望的顺序:1234 但是可能执行的时候会变成 2134 1324
但不可能是 4123!因为处理器在进行指令重排的时候会考虑数据之间的依赖性
标准的DCL懒汉式
class Singleton {
private Singleton() {
}
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
攻=> 通过反射破坏单例性
public static void main(String[] args) throws Exception {
Singleton instance = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();
System.out.println(newInstance == newInstance2);
}
防=> 私有构造方法加判断
class Singleton {
private Singleton() {
synchronized (Singleton.class) {
// 避免反射破坏
if (instance != null) {
throw new RuntimeException("不要试图使用反射破坏单例异常");
}
}
}
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
攻=> 使用反射创建两个对象来破坏单例
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();
Singleton newInstance2 = constructor.newInstance();
System.out.println(newInstance == newInstance2);
}
防=> 定义一个别人不知道的变量
class Singleton {
private static boolean manaphy = false;
private Singleton() {
synchronized (Singleton.class) {
if (!manaphy) {
manaphy = true;
} else {
throw new RuntimeException("不要试图使用反射破坏单例异常");
}
}
}
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
攻=> 通过反编译发现这个变量
public static void main(String[] args) throws Exception {
Field manaphy = Singleton.class.getDeclaredField("manaphy");
manaphy.setAccessible(true);
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();
manaphy.set(newInstance, false);
Singleton newInstance2 = constructor.newInstance();
System.out.println(newInstance == newInstance2);
}
防=> 使用枚举类
enum Singleton {
INSTANCE;
public Singleton getInstance() {
return INSTANCE;
}
}
攻=> 常规攻击
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();
Singleton newInstance2 = constructor.newInstance();
System.out.println(newInstance == newInstance2);
}
发现报错了java.lang.NoSuchMethodException: data.test.Singleton.
意思是没有无参构造
直接用idea查看Singleton反编译文件
enum Singleton {
INSTANCE;
private Singleton() {
}
public Singleton getInstance() {
return INSTANCE;
}
}
发现有私有无参构造器
使用javap -p Singleton.class
命令反编译Singleton.class文件, 发现没有构造器
final class data.test.Singleton extends java.lang.Enum<data.test.Singleton> {
public static final data.test.Singleton INSTANCE;
private static final data.test.Singleton[] $VALUES;
public static data.test.Singleton[] values();
public static data.test.Singleton valueOf(java.lang.String);
private data.test.Singleton();
public data.test.Singleton getInstance();
static {};
}
使用jad工具查看终极源码jad -sjava Singleton.class
package data.test;
final class Singleton extends Enum
{
public static Singleton[] values()
{
return (Singleton[])$VALUES.clone();
}
public static Singleton valueOf(String name)
{
return (Singleton)Enum.valueOf(data/test/Singleton, name);
}
private Singleton(String s, int i)
{
super(s, i);
}
public Singleton getInstance()
{
return INSTANCE;
}
public static final Singleton INSTANCE;
private static final Singleton $VALUES[];
static
{
INSTANCE = new Singleton("INSTANCE", 0);
$VALUES = (new Singleton[] {
INSTANCE
});
}
}
发现一个带有两个参数的构造器private Singleton(String s, int i){super(s, i);}
传入这两个参数类继续攻击
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();
Singleton newInstance2 = constructor.newInstance();
System.out.println(newInstance == newInstance2);
}
发现报java.lang.IllegalArgumentException: Cannot reflectively create enum objects
错误
攻击失败枚举类单例无法被破坏
比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
public class CASDemo {
//CAS compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//期望 更新
//public final boolean compareAndSet(int expect, int update){...}
如果期望的值达到了,那么有更新,否则就不更新 CAS 是CPU的并发原语
System.out.println(atomicInteger.compareAndSet(2020, 2021));// true
System.out.println(atomicInteger.get());// 2021
System.out.println(atomicInteger.compareAndSet(2020, 2021));// false
System.out.println(atomicInteger.get());// 2021
}
}
源码
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//发现使用Unsafe类调用的compareAndSwapInt()方法,点进去发现这个方法是本地方法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
Java无法操作内存 但可以通过 native 调用c++ 的方法
c++可以操作内存, 所以Java可以通过这个类操作内存
查看上文AtomicInteger
类的getAndIncrement()
方法源码
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//点进去查看源码,getIntVolatile()方法是本地方法,所以操作内存,效率高
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//这个do-while循环就是自旋锁 使用cas来保证原子性
缺点:
public class CASDemo {
//CAS compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//----------------------捣乱的线程start----------------------------------
System.out.println(atomicInteger.compareAndSet(2020, 2021));// true
System.out.println(atomicInteger.get());// 2021
System.out.println(atomicInteger.compareAndSet(2021, 2020));// true
System.out.println(atomicInteger.get());// 2020
//----------------------捣乱的线程end----------------------------------
//----------------------期望的线程----------------------------------
System.out.println(atomicInteger.compareAndSet(2020, 6666));// true
System.out.println(atomicInteger.get());// 6666
}
}
解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
public class AtomicStampedReferenceDemo {
static AtomicStampedReference<String> reference = new AtomicStampedReference<>("chen", 1);
public static void main(String[] args) {
new Thread(() -> {
int stamp = reference.getStamp();// 获得版本号
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
reference.compareAndSet("chen", "manaphy", reference.getStamp(), reference.getStamp() + 1);
System.out.println("a2=>" + reference.getStamp());// 第一次修改
reference.compareAndSet("manaphy", "chen", reference.getStamp(), reference.getStamp() + 1);
System.out.println("a3=>" + reference.getStamp());// 将值改回来
}, "a").start();
new Thread(() -> {
int stamp = reference.getStamp();
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference.compareAndSet("chen", "tom", stamp, stamp + 1));// 修改失败
System.out.println("b2=>" + reference.getStamp());
}, "b").start();
}
}
输出如下
a1=>1
b1=>1
a2=>2
a3=>3
false
b2=>3
再次贴出ReentrantLock
的源码
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
/**
* 非公平锁
* 非公平锁在实现的时候多次强调随机抢占
*/
static final class NonfairSync extends Sync {
...
}
/**
* 公平锁
* 实现机理在于每次有线程来抢占锁的时候,都会检查一遍有没有等待队列,如果有,按队列依次执行
*/
static final class FairSync extends Sync {
...
}
public ReentrantLock() {
sync = new NonfairSync();//默认使用非公平锁
}
public ReentrantLock(boolean fair) {//传入true使用公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
...
}
@Test
public void synchronizedTest() {
Phone phone = new Phone();
new Thread(phone::sms, "A").start();
new Thread(phone::sms, "B").start();
}
class Phone {
public synchronized void sms() {
System.out.println(Thread.currentThread().getName() + " sms");
call();// 这里调用加锁的call()方法
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " call");
}
}
结果如下
A sms
A call
B sms
B call
多次执行 发现只有A线程执行完了sms()和call()方法后线程B才开始执行
@Test
public void lockTest() {
Phone2 phone = new Phone2();
new Thread(phone::sms, "A").start();
new Thread(phone::sms, "B").start();
}
public static class Phone2 {
Lock lock = new ReentrantLock();
public void sms() {
lock.lock();
lock.lock();// lock 锁必须配对,否则就会死在里面
try {
System.out.println(Thread.currentThread().getName() + " sms");
call();// 这里调用加锁的call()方法
} finally {
lock.unlock();
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " call");
} finally {
lock.unlock();
}
}
}
执行后是同样的效果
上文 Unsafe类 里就有自旋锁
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
自定义一个自旋锁测试
package juc;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁的演示
*
* @author Manaphy
* @date 2020-07-11
*/
public class SpinlockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加锁
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "-->lock");
// 自旋锁
while (!atomicReference.compareAndSet(null, thread)) {
}
}
//解锁
public void unlock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "-->unlock");
atomicReference.compareAndSet(thread, null);
}
public static void main(String[] args) {
SpinlockDemo lock = new SpinlockDemo();
new Thread(() -> {
lock.lock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "T1").start();
new Thread(() -> {
lock.lock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "T2").start();
}
}
结果如下
T1-->lock
T2-->lock
T1-->unlock
T2-->unlock
分析: T2线程 lock 后就会进入自旋锁状态(在此期间 期望是null, 但是T1线程一直在执行, 所以不是null atomicReference.compareAndSet(null, thread)
一直返回 false ==> while循环就一直是 true) 直到T1释放锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
package juc;
import java.util.Date;
public class DeadLockTest {
public static final String obj1 = "obj1";
public static final String obj2 = "obj2";
public static void main(String[] args) {
LockA la = new LockA();
new Thread(la).start();
LockB lb = new LockB();
new Thread(lb).start();
}
}
class LockA implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockA 开始执行");
while (true) {
synchronized (DeadLockTest.obj1) {
System.out.println(new Date().toString() + " LockA 锁住 obj1");
Thread.sleep(3000); // 此处等待是给B能锁住机会
synchronized (DeadLockTest.obj2) {
System.out.println(new Date().toString() + " LockA 锁住 obj2");
Thread.sleep(60 * 1000); // 为测试,占用了就不放
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockB implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockB 开始执行");
while (true) {
synchronized (DeadLockTest.obj2) {
System.out.println(new Date().toString() + " LockB 锁住 obj2");
Thread.sleep(3000); // 此处等待是给A能锁住机会
synchronized (DeadLockTest.obj1) {
System.out.println(new Date().toString() + " LockB 锁住 obj1");
Thread.sleep(60 * 1000); // 为测试,占用了就不放
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用jps
进行排查
D:\Git\JavaProject>jps -l #定位进程号
8344 juc.DeadLockTest
13916
9884 sun.tools.jps.Jps
D:\Git\JavaProject>jstack 8344 #找到死锁问题
2020-07-11 22:36:55
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.251-b08 mixed mode):
...
Java stack information for the threads listed above:
===================================================
"Thread-1":
at juc.LockB.run(DeadLockTest.java:46)
- waiting to lock <0x0000000716c206d0> (a java.lang.String)
- locked <0x0000000716c20700> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at juc.LockA.run(DeadLockTest.java:26)
- waiting to lock <0x0000000716c20700> (a java.lang.String)
- locked <0x0000000716c206d0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.