JUC全文
多线程锁
synchronized的情况
- 非公平锁
synchronized修饰方法时,锁住调用该方法的对象- 无
synchronized修饰的方法,正常执行,不受影响 - 两个对象调用各自的
synchronized方法,不会互相影响 synchronized和static同时修饰的方法,锁住的是类的Class对象,受锁控制- 具体为三种形式
- 对于普通同步方法,锁是当前实例对象
- 对于静态同步方法,锁是当前类的Class对象
- 对于同步方法块,锁是
Synchronized括号中的对象
公平锁和非公平锁
- 非公平锁,所有任务可能只有一个线程执行
造成线程饿死,执行效率高
private final ReentrantLock lock = new ReentrantLock();
private final ReentrantLock lock = new ReentrantLock(false);
- 公平锁,效率相对较低,在执行时,需要先质询
private final ReentrantLock lock = new ReentrantLock(true);
- hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
//尾
Node t = tail; // Read fields in reverse initialization order
//头
Node h = head;
Node s;
//头尾不是一个 且 不是只有头节点 或者 当前线程不为s线程
//如果头尾是一个,那么没有线程排队
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
可重入锁
- sysnchronized,隐式可重入锁;Lock,显式可重入锁
- 进入第一道大门,拿到一个锁,里面的区域可以直接执行
- 递归锁,可以任意进入
用的同一把锁的区域
synchronized void add() {
add();
}
- lock
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "外层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "中层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "内层");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
}, "t1").start();
死锁
- 两个以上的进程,在执行过程中,因为争夺资源,造成互相等待的现象,没有外力干涉,无法执行下去
- 原因
- 系统资源不足
- 进程运行过程中,推进顺序不合适
- 资源分配不当
- AB互相等待
new Thread(() -> {
synchronized (a) {
System.out.println(Thread.currentThread().getName() + "持有A,试图获取B");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println(Thread.currentThread().getName() + "获取B");
}
}
}, "AA").start();
new Thread(() -> {
synchronized (b) {
System.out.println(Thread.currentThread().getName() + "持有B,试图获取A");
synchronized (a) {
System.out.println(Thread.currentThread().getName() + "获取A");
}
}
}, "BB").start();
- 验证是否是死锁
- jps 查看java进程
- jstack 跟踪堆栈信息
sychronized和ReentrantLock的区别
sychronized关键字,ReentrantLock类sychronized会自动加锁和释放锁,ReentrantLock需要手动加锁和释放锁sychronized是JVM层面的锁,ReentrantLock是API层面的锁sychronized非公平锁,ReentrantLock可以选择公平锁和非公平锁
Callable接口
- 创建线程的方式
- 继承Thread
- 实现Runnable接口
- Callable接口
- 线程池
- 可以获得线程返回结果
- Runnable接口和Callable接口
- R无返回值;C有返回值
- R不抛出异常;C抛出异常
- R需要实现
run();C需要实现call()
- 通过代理实现线程创建,同时和
Runnable和Callable有关系
Runnable有一个实现类FutureTaskFutureTask有可以传入Callable接口的构造
- FutureTask原理
- 不影响主线程的执行,开线程去完成耗时任务;需要任务结果时,直接获取
- 将一个耗时任务,分成多个部分,分别执行;依次汇总,不影响主线程
- 开的线程,会把结果保存
FutureTask<Integer> ft = new FutureTask<>(() -> {
System.out.println(Thread.currentThread().getName());
return 24;
});
new Thread(ft, "ft1").start();
while (!ft.isDone()) { //是否完成
System.out.println("ft hasn't finished.");
}
//得到返回结果,可以获取多次
System.out.println(ft.get());
System.out.println(ft.get());
System.out.println(ft.get());
辅助类
CountDownLatch
- 减少计数,设置一个计数器,通过
countDown进行减1操作,使用await方法等待计数器不大于0,继续执行await后面的语句
//设置计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 100; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "离开");
countDownLatch.countDown();
}, "同学" + i).start();
}
countDownLatch.await();
System.out.println("班长走了");
构造
- 初始化个数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
// 内部类,记录个数和状态,并维护
// 继承了AQS队列
this.sync = new Sync(count);
}
释放资源
- 实际调用
tryReleaseShared修改个数,调用doReleaseShared();修改节点状态,唤醒后面的节点
public void countDown() {
sync.releaseShared(1);
}
releaseShared每个线程都可以直接设置
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
// 释放资源,并唤醒后面的一个线程,实际上就是唤醒await的线程
doReleaseShared();
return true;
}
return false;
}
- CAS操作,修改个数,能设置为0后,返回true
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
doReleaseShared设置头节点的状态为PROPAGATE,并唤醒后面的一个线程,实际上就是尝试唤醒持有await的线程
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
// 将h设置为PROPAGATE,会在unparkSuccessor被设置为0,并唤醒后继节点的线程
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
获取资源
- 阻塞
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
acquireSharedInterruptibly可响应中断
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//说明可以响应阻塞
if (Thread.interrupted())
throw new InterruptedException();
// (getState() == 0) ? 1 : -1; 0 返回1 非0返回-1
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
doAcquireSharedInterruptibly尝试获取资源
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
// 共享节点加入等待队列,这里会创建2个节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
// 如果此时只有一个线程等待,那么p就为头节点
final Node p = node.predecessor();
if (p == head) { //该节点前面的一个节点是头节点
//查看当前的同步状态
int r = tryAcquireShared(arg);
if (r >= 0) {
// 返回1说明计数为0,获取资源成功
// 更新头节点,并释放头节点
// 打破循环,跳出
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 如果没有,尝试阻塞,并挂起阻塞
// 会设置前面的节点为SIGNAL
// 如果前面的节点为cancel,会进行清理
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate,唤醒后面的节点
- 初始化时头节点为0,会被设置为
PROPAGATE时,在唤醒时,下一个节点为实际await- 如果下一个节点为Shared,同为
await节点,那么也会被唤醒,并将前一个节点设置为PROPAGATE,让下一个节点进行出队操作
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
// 设置为新的头节点
setHead(node);
// propagate > 0 已经获得资源,需要唤醒后面的节点
// h.waitStatus < 0 旧的头节点后面,可以被唤醒
// 新的头节点的后面的节点可以被唤醒
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
// 获取后继节点,并进行唤醒
Node s = node.next;
if (s == null || s.isShared())
// 唤醒后继节点
doReleaseShared();
}
}
CyclicBarrier
- 循环栅栏,一组线程互相等待,直到达到某个公共屏障点的数量达到要求后,执行指定方法
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
//固定数量的线程停到栅栏前执行
//执行该方法的线程是,最后一个到达的线程
System.out.println(Thread.currentThread().getName() + "达到CB");
});
for (int i = 0; i < 7; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "醒来");
//线程停在了栅栏前
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "name" + i).start();
}
原理
- 构造方法
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 临时保存
this.parties = parties;
// 计数
this.count = parties;
// 回调
this.barrierCommand = barrierAction;
}
- 控制屏障的循环,如果
generation.broken为true,当有线程await时,抛出异常
private Generation generation = new Generation();
await
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
dowait通过ReentrantLock上锁,保证一次只有一个线程调用
- 当有线程
await时,int index = --count;,如果count不为0,没有broken,没有中断,时间没到则trip.await();/nanos = trip.awaitNanos(nanos);等待count==0,执行回调方法command.run();,唤醒所有线程,并还原计数nextGeneration();
nextGeneration
private void nextGeneration() {
// signal completion of last generation
// 唤醒所有线程AQS
trip.signalAll();
// set up next generation
// 恢复count
count = parties;
// 重新创建generation
generation = new Generation();
}
Semaphore
- 信号灯,计数信号量,维护了一个许可集。
acquire()获取许可,如果获取不到,阻塞线程;release()释放许可,线程继续执行
//3个许可
Semaphore semaphore = new Semaphore(3);
//6辆汽车
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
//抢占车位
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, "car - " + i).start();
}
原理
- 构造,非公平锁
public Semaphore(int permits) {
// 相当于设置count
sync = new NonfairSync(permits);
}
NonfairSync继承了Sync,重写了tryAcquireShared
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
获取资源
acquire
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
acquireSharedInterruptibly最终调用重写的nonfairTryAcquireShared
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 如果剩余的量 < 0 后面的节点都可以全部唤醒
if (tryAcquireShared(arg) < 0)
// 后面的节点尝试获取节点,失败则挂起
doAcquireSharedInterruptibly(arg);
}
nonfairTryAcquireShared获取资源,直接抢,不排队
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 当前的量
int available = getState();
// 如果设置生成,剩下的
int remaining = available - acquires;
// 剩下为负数 || CAS设置成功,返回剩下的
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
释放资源
release
public void release() {
sync.releaseShared(1);
}
releaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//唤醒一个共享节点
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared直接归还
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
总结
CountDownLatch,其他线程获取资源,count-1,主线程争抢资源Semaphore,线程之间争抢资源,争抢不到的挂起,头节点争抢;处理完毕的线程,归还资源CyclicBarrier,计数,等掉调用await的线程个数,满足条件,然后全部唤醒signalAll