ReentrantLock(重入锁)
- 可重入:单线程可以重复进入(次数),但要重复退出(相同次数)
public class TestReentrantLock implements Runnable {
private static ReentrantLock lock = new ReentrantLock();
private static int i = 0;
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
lock.lock();
lock.lock();
try {
i++;
} finally {
lock.unlock();
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
TestReentrantLock tl = new TestReentrantLock();
Thread t1 = new Thread(tl);
Thread t2 = new Thread(tl);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
单线程中,可重复进行lock()
获得锁(内部记录次数),同样的需要执行相同次数
的unlock()
。若lock>unlock,则线程结束后,其他线程无法获得锁从而阻塞;若lock<unlock,则会抛出IllegalMonitorStateException
,监视器状态异常。
- 可中断:lockInterruptibly()可中断式地加锁
public class TestReentrantLockInterrupt implements Runnable {
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
private int lock;
/**
* 控制加锁顺序,方便构成死锁
*/
public TestReentrantLockInterrupt(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if (lock == 1) {
// 可中断的加锁
lock1.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread())
lock1.unlock();
if (lock2.isHeldByCurrentThread())
lock2.unlock();
System.out.println(Thread.currentThread().getId() + ":线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
TestReentrantLockInterrupt tl1 = new TestReentrantLockInterrupt(1);
TestReentrantLockInterrupt tl2 = new TestReentrantLockInterrupt(2);
Thread t1 = new Thread(tl1);
Thread t2 = new Thread(tl2);
t1.start();
t2.start();
Thread.sleep(1000);
DeadlockChecker.check();
}
}
死锁检查类DeadlockChecker.java
public class DeadlockChecker {
private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
final static Runnable deadlockCheck = new Runnable() {
@Override
public void run() {
while (true) {
long[] deadlockedThreadIds = mbean.findDeadlockedThreads();
if (deadlockedThreadIds != null) {
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds);
for (Thread t : Thread.getAllStackTraces().keySet()) {
for (ThreadInfo threadInfo : threadInfos) {
if (t.getId() == threadInfo.getThreadId()) {
t.interrupt();
}
}
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
}
};
public static void check() {
Thread t = new Thread(deadlockCheck);
t.setDaemon(true);
t.start();
}
}
注:当线程1和线程2形成死锁(使用可中断的加锁
)时,使用Interrupt发送中断信号,可以中断线程,进入Catch块。
- 可限时:超时不能获得锁,就返回false,不会永久等待构成死锁。
public class TestReentrantLockTime implements Runnable {
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
Thread.sleep(6000);
} else {
System.out.println("Get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread())
lock.unlock();
}
}
public static void main(String[] args) {
TestReentrantLockTime tl = new TestReentrantLockTime();
Thread t1 = new Thread(tl);
Thread t2 = new Thread(tl);
t1.start();
t2.start();
}
}
注:线程1线程2都需要同一把锁,拿不到锁的线程超时就会返回false,进入else分支。
- 公平锁:先来先得。若不是公平锁,则先申请锁的,不一定先拿到锁;反之,公平锁一定是先来先得。公平锁性能不如非公平锁,处理排队。
// ReentrantLock的一个构造函数
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Condition(条件)
Synchronized
和Object.wait/Object.notify
配合使用。
Conditaion
和ReentrantLock
配合使用。
public class TestReentrantLockCondition implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
@Override
public void run() {
try {
lock.lock();
condition.await();
System.out.println("Thread is going on");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread())
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
TestReentrantLockCondition tl = new TestReentrantLockCondition();
Thread t1 = new Thread(tl);
t1.start();
Thread.sleep(2000);
// 通知线程t1继续运行
lock.lock();
condition.signal();
lock.unlock();
}
}
说明:(1)线程1拿到锁(2)线程1等待条件,暂时释放锁(3)主线程拿到锁(4)主线程通知条件(5)主线程释放锁(6)线程1拿到锁(多个线程需要竞争锁),继续运行。
注:Condition.awaitUninterruptibly()
,不会响应中断标志,Object.wait()当遇到中断标记时,会抛出异常。
Semaphore(信号量)
共享锁,可以允许多个线程进入临界区(PV操作中的信号量),带容量的。
- void acquire():阻塞式的请求信号量。
- void acquireUninterruptibly():不可中断阻塞式的请求信号量。
- boolean tryAcquire():非阻塞请求信号量,立马返回。
- boolean tryAcquire(long timeout,TIneUnit unit):阻塞式限时请求信号量,超时或成功返回结果。
- void release():释放信号量。
public class TestSemapDemo implements Runnable {
private final Semaphore semp = new Semaphore(5);
private AtomicInteger i = new AtomicInteger(0);
@Override
public void run() {
try {
semp.acquire();
// 模拟耗时操作
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId() + ":done " + i.incrementAndGet() + " !");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semp.release();
}
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(20);
final TestSemapDemo demo = new TestSemapDemo();
for (int i = 0; i < 20; i++) {
pool.submit(demo);
}
}
}
代码说明:初始化了容量为5的信号量,每次请求都会减少1个,释放增加一个(回忆PV操作的信号量即可)。
ReadWriteLock(读写锁)
ReadWriteLock是JDK5提供的读写分离锁。
读-读不互斥:读读之间不阻塞。
读-写互斥:读阻塞写,写也会阻塞读。
写-写互斥:写写阻塞。
读 | 写 | |
---|---|---|
读 | 非阻塞 | 阻塞 |
写 | 阻塞 | 阻塞 |
// 主要方法
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
// 读写锁的方法和ReentrantLock一致.
// lock还可以获得读锁的数量,判断写是否被锁定等方法
// public int getReadLockCount()
// public boolean isWriteLocked()
// 拿到等待线程列表等
// 具体可看读写锁,和面用到在详细记录.
CountDownLatch(倒数计时器)
构造函数初始化一个数量,每次countDown,都会减1(CAS操作)。
public class TestCountDownLatch implements Runnable {
static final CountDownLatch end = new CountDownLatch(10);
static final TestCountDownLatch demo = new TestCountDownLatch();
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete!");
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
threadPool.submit(demo);
}
// 等待检查
end.await();
System.out.println("BOOM!");
threadPool.shutdown();
}
}
类似于方便的同步操作,先序任务完成后,后续线程才可以进行。
CyclicBarrier(循环栅栏)
Cyclic意为循环,也就是说栅栏可以重复使用。比如,设置为10,一批10个线程集合后,计数器归零,执行指定操作,然后进行下一轮集合。
构造函数:public CyclicBarrier(int parties, Runnable barrierAction)
,栅栏的大小和一次循环后需要执行的操作。
public class TestCyclicBarrier {
public static class Soldier implements Runnable {
private String soldier;
private final CyclicBarrier barrier;
Soldier(CyclicBarrier barrier, String name) {
this.soldier = name;
this.barrier = barrier;
}
@Override
public void run() {
try {
barrier.await();
doWork();
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
}
}
private void doWork() {
try {
Thread.sleep(Math.abs(new Random().nextInt() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(soldier + "完成任务!");
}
}
public static class BarrierRun implements Runnable {
boolean flag;
int N;
public BarrierRun(boolean flag, int n) {
this.flag = flag;
N = n;
}
@Override
public void run() {
if (flag) {
System.out.println("司令:士兵" + N + "个,任务完成!");
flag = false;
} else {
System.out.println("司令:士兵" + N + "个,集合完毕!");
flag = true;
}
}
}
public static void main(String[] args) {
final int N = 10;
Thread[] allSoldier = new Thread[N];
boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(N, new BarrierRun(flag, N));
System.out.println("集合队伍!");
for (int i = 0; i < N; i++) {
System.out.println("士兵" + i + "报道");
allSoldier[i] = new Thread(new Soldier(barrier, "士兵" + i));
allSoldier[i].start();
//if (i == 5) {
// allSoldier[0].interrupt();
//}
}
}
}
10个士兵集合完毕,才可以执行任务,同步问题,用CyclicBarrier可以实现,且可以完成多组士兵集合执行任务。
(1)首先,士兵开始集合,进行await(),阻塞式的等待。
(2)10次后,激活Runnable任务,集合完毕。
(3)执行任务,进行await(),阻塞式的等待。
(4)10次后,激活Runnable任务,任务完成。
这个任务是可以中断的,当中断后抛出InterruptedException
异常,而等待在该栅栏上的其他线程会抛出BrokenBarrierException
异常(不会集合完成,没有意义的等待)。
LockSupport
提供线程阻塞原语。
- LockSupport.park():挂起线程
- LockSupport.unpark(t1):停止挂起线程
与suspend()的区别:不容易引起线程冻结(resume发生在suspend前,后调用suspend造成永久性阻塞)。
park是不会抛出异常
的,但是会响应中断
(Interrupt)标记。
park后主要有两种方式可以继续运行,(1)Thread.unpark(t1)(2)t1.interrupt
public class TestLockSupport {
private static final Object u = new Object();
private static ChangeObjectThread t1 = new ChangeObjectThread("t1");
private static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread {
ChangeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized (u) {
System.out.println("in " + getName());
LockSupport.park();
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
t2.start();
Thread.sleep(100);
// t2.interrupt();
// t1.interrupt();
LockSupport.unpark(t1);
LockSupport.unpark(t2);
t1.join();
t2.join();
}
}
ReentrantLock的实现
(1)内部通过CAS状态判断是否修改成功。
// 非公平锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
比较和设置状态State,成功则拿到排他锁,立即返回。
(2)如果没拿到锁,线程就要进入等待队列,队列中的线程都要执行park
。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
源码中可以看到,最后没有拿到锁,队列中的线程需要去parkAndCheckInterrupt,也就是park去挂起。