JUC编程Tools
包含五个工具类: Excutors、Semaphore、Exchanger、CyclicBarrier、CountDownLatch
Semaphore
信号量的意思,控制访问特定资源的线程数目。
用于流量控制,限制最大的并发的并发访问数。公共资源池定义 X 个permit
共享锁,非公平锁 ===> STATE(AQS)
2.1 构造方法
public Semaphore(int permits)
public Semaphore(int permits, boolean fair) permits 表示许可线程的数量 fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程
2.2 重要方法
public void acquire() throws InterruptedException
public void release()
tryAcquire(long timeout, TimeUnit unit)
- acquire() 表示阻塞并获取许可
- release() 表示释放许可
Semaphore 常用方法
Semaphore semaphore = new Semaphore(2);
void semaphore.acquire();
/* @param permits the number of permits to acquire
* @param timeout the maximum time to wait for the permits
* @param unit the time unit of the {@code timeout} argument */
semaphore.tryAcquire(1, 1000, TimeUnit.MILLISECONDS);
semaphore.release();
举例
SemaphoreSample.java
import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * @description :可用于流量控制,限制最大的并发访问数 */ public class SemaphoreSample { public static void main(String[] args) { Semaphore semaphore = new Semaphore(2); for (int i=0;i<5;i++){ new Thread(new Task(semaphore,"yangguo+"+i)).start(); } } static class Task extends Thread{ Semaphore semaphore; public Task(Semaphore semaphore,String tname){ this.semaphore = semaphore; this.setName(tname); } public void run() { try { // semaphore.tryAcquire(1, 1000, TimeUnit.MILLISECONDS); semaphore.tryAcquire(); System.out.println(Thread.currentThread().getName()+":aquire() at time:"+System.currentTimeMillis()); Thread.sleep(1000); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
从打印结果可以看出,一次只有两个线程执行 acquire(),只有线程进行 release() 方法后 才会有别的线程执行 acquire()。
CountDownLatch
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例 如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
CountDownLatch如何工作?
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当 一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的 线程已经完成了任务,
然后在闭锁上等待的线程就可以恢复执行任务。
CountDownLatch应用场景例子
比如陪媳妇去看病。 医院里边排队的人很多,如果一个人的话,要先看大夫,看完大夫再去排队交钱取药。 现在我们是双核,可以同时做这两个事(多线程)。
假设看大夫花3秒钟,排队交费取药花5秒钟。我们同时搞的话,5秒钟我们就能完成,然后 一起回家(回到主线程)。
/** * 看大夫任务 */ public class SeeDoctorTask implements Runnable { private CountDownLatch countDownLatch; public SeeDoctorTask(CountDownLatch countDownLatch){ this.countDownLatch = countDownLatch; } public void run() { try { System.out.println("开始看医生"); Thread.sleep(3000); System.out.println("看医生结束,准备离开病房"); } catch (InterruptedException e) { e.printStackTrace(); }finally { if (countDownLatch != null) countDownLatch.countDown(); } } } /** * 排队的任务 */ public class QueueTask implements Runnable { private CountDownLatch countDownLatch; public QueueTask(CountDownLatch countDownLatch){ this.countDownLatch = countDownLatch; } public void run() { try { System.out.println("开始在医院药房排队买药...."); Thread.sleep(5000); System.out.println("排队成功,可以开始缴费买药"); } catch (InterruptedException e) { e.printStackTrace(); }finally { if (countDownLatch != null) countDownLatch.countDown(); } } } /** * 配媳妇去看病,轮到媳妇看大夫时 * 我就开始去排队准备交钱了。 */ public class CountDownLaunchSample { public static void main(String[] args) throws InterruptedException { long now = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(new SeeDoctorTask(countDownLatch)).start(); new Thread(new QueueTask(countDownLatch)).start(); //等待线程池中的2个任务执行完毕,否则一直 countDownLatch.await(); System.out.println("over,回家 cost:"+ (System.currentTimeMillis()now)); } }
Executors
主要用来创建线程池,代理了线程池的创建,使得你的创建入口参数变得简单
重要方法
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需 要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的 线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执 行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作 线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行