Semaphore
简介
- 无论是内部锁
synchronized
还是 ReentrantLock
,一次都只允许一个线程访问一个资源 Semaphore
是对锁的扩展,允许多个线程同时访问某一资源
源码
- 构造函数
public Semaphore(int permits)
:参数 permits
指定Semaphore
的准入数,即同时能申请多少个许可。默认是非公平锁public Semaphore(int permits, boolean fair)
:第二个参数是指定时候为公平锁
- 逻辑方法:默认每一个线程每次只申请一个许可证,相当于指定了同时有多少线程可以访问某一个资源
public void acquire()
:尝试获取,没有,则线程等待;等待的过程中总会响应线程中断public void acquireUninterruptibly()
:与acquire()
类似,只是不会响应中断public boolean tryAcquire()
:尝试获取,没有,则立即返回,不等待public boolean tryAcquire(long timeout, TimeUnit unit)
:尝试获取,没有,等待public void release()
释放
- 注意:一个线程其实可以设置获取多个许可(只有当每一个线程每次只申请),如以下组方法:
public void acquire(int permits)
public void acquireUninterruptibly(int permits)
public void release(int permits)
示例
- 简单示例
// 创建线程池,newCachedThreadPool,在没有空闲的情况下,有多少任务,创建多少线程
ExecutorService service = Executors.newCachedThreadPool();
// 建立信号灯,有参数fair,实现线程进入优先级。true的时候,先到先进
final Semaphore sp = new Semaphore(3);
//创建10个线程
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
sp.acquire();//获取信号灯
} catch (InterruptedException e) {
e.printStackTrace();
}
//sp.availablePermits() 被拿走的灯的个数
System.out.println("线程" + Thread.currentThread().getName() +
"进入,当前已有" + (3 - sp.availablePermits()) + "线程");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"即将离开");
sp.release();//释放获取的灯
//下面代码有时候执行不准确
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (3 - sp.availablePermits()) + "线程");
}
};
//执行10个线程
service.execute(runnable);
}
输出:
线程pool-1-thread-1进入,当前已有1线程
线程pool-1-thread-2进入,当前已有2线程
线程pool-1-thread-3进入,当前已有3线程
线程pool-1-thread-1即将离开
线程pool-1-thread-4进入,当前已有3线程
线程pool-1-thread-1已离开,当前已有3线程
线程pool-1-thread-2即将离开
线程pool-1-thread-5进入,当前已有3线程
线程pool-1-thread-2已离开,当前已有3线程
线程pool-1-thread-4即将离开
线程pool-1-thread-4已离开,当前已有2线程
线程pool-1-thread-5即将离开
线程pool-1-thread-5已离开,当前已有1线程
线程pool-1-thread-3即将离开
线程pool-1-thread-3已离开,当前已有0线程
- 简单实现一个对象池
public class SemaphorePool {
private static final int MAX_AVAILABLE = 100;
/**
* 最大可以有 100 个许可
*/
private final Semaphore available = new Semaphore(MAX_AVAILABLE);
public Object getItem() throws InterruptedException {
// 申请一个许可
// 同时只能申请 100 个线程进入取得可用项,超过100个则需要等待
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x)) {
// 释放一个许可证
available.release();
}
}
/**
* 存放对象
*/
Object[] items = new Object[MAX_AVAILABLE];
Boolean[] used = new Boolean[MAX_AVAILABLE];
private Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; i++) {
// 如果当前项未被使用,则获得它
if (!used[i]) {
// 将当前项标记为已经使用
used[i] = true;
return items[i];
}
}
return null;
}
private synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; i++) {
// 找到给定项的索引
if (item == items[i]) {
if (used[i]) {
// 将给定项标记为未被使用
used[i] = false;
return true;
} else {
return false;
}
}
}
return false;
}
}
CountDownLatch
简介
CountDownLatch
:闭锁、倒计时门栓。可以实现如一个或多个线程一直等待,直到其他线程的操作执行完后再执行。同样的功能类似与主线程提交一组任务,等所有子线程任务完成后,主线程再次执行,期间一直阻塞。zookeeper
的原生watcher
就可以使用CountDownLatch
实现- 我们自己写多线程并发测试,可以使用
CountDownLatch
,一定程度上模拟实现并发
源码API
- 构造函数:
public CountDownLatch(int count)
count
代表等待的线程(await())被count
个线程提醒,会开始执行
await()
:等待,执行该方法的线程会阻塞等待await(long timeout, TimeUnit unit)
:等待,超时后抛出异常countDown()
:提醒,调用该方法提醒,会给阻塞的方法一个提醒(其实是内部计数器减1),当计数器(count)减为0,阻塞线程开始执行getCount()
:获取计数器
示例
- 简单例子
public class CountDownLatchFirst {
public static void main(String[] args) throws InterruptedException {
CountDownLatchFirst count = new CountDownLatchFirst();
count.test1();
}
void test1() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new MyThread("Main", countDownLatch));
executor.execute(new MyThread("Sub", countDownLatch));
// main方法线程等待上面的两个线程完成后才会执行
countDownLatch.await();
System.out.println("~~~ 可以执行了 ~~~");
}
class MyThread implements Runnable {
private final String name;
private final CountDownLatch latch;
public MyThread(final String name, final CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + name + " 完成工作");
latch.countDown();
}
}
}
zookeeper
的watcher
实现
public class ZkClientSecond implements Watcher {
CountDownLatch downLatch = new CountDownLatch(1);
void getZkClient() throws Exception {
zkClient = Objects.isNull(zkClient) ? new ZooKeeper(connectString, 2000, this) : zkClient;
downLatch.await();
}
@Override
public void process(WatchedEvent event) {
System.out.println("状态 : " + event.getState());
System.out.println("path : " + event.getPath());
System.out.println("已经触发了 " + event.getType() + " 事件!");
if (Event.KeeperState.SyncConnected == event.getState()) {
downLatch.countDown();
switch (event.getType()) {
case NodeCreated:
System.out.println("NodeCreated~~~");
break;
case NodeDeleted:
System.out.println("NodeDeleted~~");
break;
case NodeDataChanged:
System.out.println("NodeDataChanged~~");
break;
case NodeChildrenChanged:
System.out.println("NodeChildrenChanged~~");
break;
default:
System.out.println("default ~~~");
}
}
}
}
- 模拟并发访问
/**
* 模拟2000 并发
*/
private static final int THREAD_NUM = 2000;
/**
* 使用 CountDownLatch 尽量的模拟并发
*/
private CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
public void doService() throws InterruptedException {
// 创建,并不是马上发起请求
Thread[] threads = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++) {
// 多线程模拟用户查询请求
Thread thread = new Thread(() -> {
try {
// 代码在这边等待,等待countDownLatch 的计数器为0,再运行后续代码
countDownLatch.await();
// 调用业务方法
service.doService(name);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threads[i] = thread;
thread.start();
// 计数器递减
countDownLatch.countDown();
}
for (Thread thread : threads) {
thread.join();
}
}
CyclicBarrier
- CyclicBarrier 可以实现类似
集合出发
的功能,也可以用来实现模拟并发
/**
* 模拟2000 并发
*/
private static final int THREAD_NUM = 50;
/**
* 使用 CyclicBarrier 尽量的模拟并发
*/
private CyclicBarrier cyclicBarrier = new CyclicBarrier(THREAD_NUM);
public void createOrder2() throws InterruptedException {
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
// 模拟分布式环境
OrderService orderService = new OrderServiceImplWithLock();
try {
// 代码在这边等待一起出发
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
// 调用业务方法
orderService.createOrder();
}).start();
}
Thread.sleep(50000L);
}
Exchanger
参考
- 源码地址