ReentrantLock
可重入锁,应用层面的锁,jdk6后性能和synchronized差不多,但是有更多的功能。
- 可以响应线程中断:
ReentrantLock lock = new ReentrantLock();
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
- 可以限时,这样可以避免死锁
ReentrantLock lock = new ReentrantLock();
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
} else {
}
} catch (InterruptedException e) {
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
- ReentrantLock可以实现公平锁:
ReentrantLock lock = new ReentrantLock(true);
- 通过Condition类来挂起唤醒线程,condition和Object的wait和notify很像
Condition condition = lock.newCondition();
condition.await();
condition.await(等待时间);
condition.awaitUninterruptibly();
condition.signal();
condition.signalAll();
Semaphore信号量
信号量可以看做共享锁,允许多个线程进入临界区
- acquire()
- acquireUninterruptibly()
- tryacquire()、tryacquire(long,TimeUnit)
- release()
Semaphore semaphore = new Semaphore(5);
try {
semaphore.acquire();
} catch (InterruptedException e) {
} finally {
semaphore.release();
}
ReadWriteLock读写锁
jdk5提供的一种读写分离的锁,有使用写锁的线程竞争锁才会加锁
- readLock()获得ReentrantReadWriteLock.ReadLock对象
- writeLock()获得ReentrantReadWriteLock.WriteLock对象
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();
readLock.lock();//读锁和写锁的使用和ReentrantLock一样:
writeLock.lock();
CountDownLatch倒数计时器
使用CountDownLatch将主线程挂起,准备工作完毕唤醒主线程
public class CountDownLatchDemo implements Runnable{
private static CountDownLatch latch = new CountDownLatch(5);
@Override
public void run() {
//一些检查工作,做完之后倒数计数器减一
latch.countDown();
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
exec.submit(new CountDownLatchDemo());
}
latch.await();//五个检查工作都完成才会继续执行
}
}
CyclicBarrier循环栅栏
和CountDownLatch很像,只不过这计数器可以重复使用,比如将计数器设为10,执行完一批十个线程后,计数器就会归零,然后凑齐下一批十个线程。
CountDownLatch是一个线程等待多个线程,CyclicBarrier可以多个线程间相互等待,而且可以复用,功能更强。
- 构造方法:
CyclicBarrier(int parties, Runnable barrierAction)
parties是计数器的数,barrierAction是计数器归零后执行的线程(回调线程)
- await()
每次调用await()的时候只有总共调用parties次await()才会继续向下执行
public class BarrierDemo implements Runnable {
int flag = 0;
@Override
public void run() {
if (flag == 0) {
//TODO
} else {
//TODO
}
flag++;
}
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(10, new BarrierDemo());
for (int i = 0; i < 10; i++) {
new Thread(new Prepare(barrier)).start();
}
Thread.sleep(5000);
}
}
class Prepare implements Runnable {
CyclicBarrier barrier;
public Prepare(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
barrier.await();//十个线程全部await之后,继续向下执行并触发barrier的执行线程,并清零计数器
//TODO
barrier.await();//复用计数器开始下一轮
//TODO
} catch (InterruptedException ie) {
} catch (BrokenBarrierException be) {//有线程被中断后,其他的线程抛出该异常
}
}}
LockSupport
提供线程阻塞原语,jdk内部有很多东西都是用LockSupport实现的。suspend()方法已经被弃用,用这个工具挂起线程是最好的。类似于信号量,内部有一个许可,pack()拿到许可,unpack()申请许可。unpack()发生在pack()之前,pack()并不会阻塞线程,和suspend()不一样。
当线程被中断时pack()会返回,线程继续执行,并不会抛出InterruptedException,pack()之后可以检测线程的中断标志位。
- LockSupport.pack()
挂起当前线程
- LockSupport.unpack(Thread)
恢复某个线程