线程同步工具集
在前面了解了线程的同步机制,临界区等,了解了线程的两种基本的同步机制:
- synchronized关键字;
- Lock接口以及其实现类:ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantLock.WriteLock
在接下来将要了解到的是更高级的同步机制,主要有以下几种:
- Semaphores(信号量):通过计数来控制共享资源的访问,该机制在大多数编程语言中都提供了;
- CountDownLatch(闭锁):该机制允许线程等待多个操作终止;
- CyclicBarrier(关卡):该机制允许多个线程在同一个点同步;
- phaser(阶段):该机制将线程同步任务分成多个阶段,所有的线程必须按照阶段顺序一个个的完成;该特性是Java 7 的新特性;
- Exchanger(交换):该机制提供了多个线程交换的一个数据点;
信号量(semaphore)机制是一个通用的线程同步机制,而其它几个线程同步工具需要根据自己的应用场景选择符合其特性的同步机制;
信号量机制其实就是使用一个内部计数器,当该数值大于0时,表明还有可以访问的共享资源,当一个线程进入到临界区时,计数器就会减一,当数值为0时,表示已经没有可以访问的共享资源了,在有线程进来访问将会被挂起;semaphore构造函数中可以传入一个数值,也可以称为”许可“,当该数值为1时,通常称为”binary semphore",因为只有0 和 1 ;
下面的简单示例中展示了如何使用semaphores同步共享数据;
动手实现
(1)创建一个PrintQueue
public class PrintQueue {
private final Semaphore semaphore;
public PrintQueue() {
// binary semaphore
this.semaphore = new Semaphore(1);
}
public void printJob(){
try {
semaphore.acquire();
long duration=(long)(Math.random()*10);
System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",
Thread.currentThread().getName(),duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
}
(2)创建一个执行线程
public class Job implements Runnable {
private PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
System.out.printf("%s: Going to print a job\n",
Thread.currentThread().getName());
printQueue.printJob();
System.out.printf("%s: The document has been printed\n",
Thread.currentThread().getName());
}
}
(3)Main
public class Main {
public static void main(String[] args) {
PrintQueue printQueue=new PrintQueue();
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(new Job(printQueue), "Thread_" + i);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
要点
当进入同步代码块时
1.首先执行 acquire()获取锁
2.执行相应业务操作
3.执行release()方法,释放锁;
该方法的构造函数中还有一个参数是fairness,可以参看前面记录的fairness的作用;
该类中还有两个方法
- acquireUninterruptibly(): 由于acquire()方法在计数器为0时,线程将被阻塞,在这期间线程可能被interrupt,所以会抛出InterrupteException异常,但是使用该方法可以避免抛出异常;
- tryAcquire():该方法返回boolean类型,可以控制不让线程进入阻塞状态;