concurrent包的同步控制工具

96
肥肥小浣熊
2017.11.06 20:24* 字数 519

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)

恢复某个线程

肥肥小浣熊
码哥
Gupao