JUC
JUC包下的所有类都是线程安全的,JUC下有:
1.ReetrantLock(可重入锁)
a> lock一定要放在 try 之前
b> 在finally 一定要释放锁
2.Semaphore(信号量)
可以实现限流功能。
代码:模拟使用Semaphore
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);//必须初始化
//创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,6, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100));
//执行任务1
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达停车场");
try {
Thread.sleep(1000);
//尝试获取锁
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行到此,表示已经获取到了车位
System.out.println(Thread.currentThread().getName()+"进入停车场");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"离开停车场");
semaphore.release();
}
});
//执行任务2
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达停车场");
try {
Thread.sleep(1000);
//尝试获取锁
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行到此,表示已经获取到了车位
System.out.println(Thread.currentThread().getName()+"进入停车场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"离开停车场");
//释放锁
semaphore.release();
}
});
//执行任务3
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达停车场");
try {
Thread.sleep(1000);
//尝试获取锁
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行到此,表示已经获取到了车位
System.out.println(Thread.currentThread().getName()+"进入停车场");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"离开停车场");
//释放锁
semaphore.release();
}
});
//执行任务4
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达停车场");
try {
Thread.sleep(1000);
//尝试获取锁
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行到此,表示已经获取到了车位
System.out.println(Thread.currentThread().getName()+"进入停车场");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"离开停车场");
//释放锁
semaphore.release();
}
});
}
}
运行结果:可以看到 Semaphore 的作用就是控制每次最多只能进去两个线程
3.CountDownLatch(计数器)
等待所有线程进入到某个步骤之后,再统一执行某个流程。这就好像跑步比赛,当所有选手都到达终点之后再宣布成绩。
CountDoenLatch执行原理:内部有一个计数器,执行 countDown 时,计数器-1,直到减到0 ,那么这个计数器就是有完毕了,就可以执行 await 之后的代码了。
代码:模拟使用CountDownLatch
/*
模拟 CountDownLatch
*/
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) throws InterruptedException {
//创建 CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(3);//创建计数器为3
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100));
executor.execute(new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(5);
num+=1;
try {
System.out.println(Thread.currentThread().getName()+"开跑");
Thread.sleep(num*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"到达了终点");
//计数器-1
countDownLatch.countDown();
}
});
executor.execute(new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(5);
num+=1;
try {
System.out.println(Thread.currentThread().getName()+"开跑");
Thread.sleep(num*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"到达了终点");
//计数器-1
countDownLatch.countDown();
}
});
executor.execute(new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(5);
num+=1;
try {
System.out.println(Thread.currentThread().getName()+"开跑");
Thread.sleep(num*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"到达了终点");
//计数器-1
countDownLatch.countDown();
}
});
//等待计数器为0,再去执行后面的代码
countDownLatch.await();
System.out.println("比赛结束");
}
}
运行结果:
4.CyclicBarrier(循环屏障)
执行原理:内有一个计数器,每次线程执行await 方法的时候,计数器 -1;
代码:模拟CyclicBarrier
import java.util.Random;
import java.util.concurrent.*;
public class Demo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("------到达了屏障--------");
}
});
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100));
for (int i = 0; i < 4; i++) {
int fi = i;
executor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(fi*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开跑");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束");
}
});
}
}
}
运行结果:
CountDownLatch 和 CyclicBarrier 的区别:
答:实现原理一样,CyclicBarrier可以反复使用,而CountDownLatch 只能使用一次。
小结:
- ReetrantLock
- Semaphore(信号量)(限流)acquire 获取锁,release 释放锁
- CountDownLatch计数器(执行使用一次)countdown()-1 直到计数器为0才执行 await 之后的代码
- CyclicBarrier (循环屏障)会阻塞等待线程的数量,只有满足屏障的数量才能继续往下执行