java并发值多线程同步业务场景以及解决方案
1.20个人排队同时访问2个购票窗口,同时能购票的只有两个人,当其中一个人买票完成后,18个人中的其中一个在占用窗口进行购买。
20个人相当于20个线程,2相当于资源,当18个人等待的时候,相当于线程堵塞,
问题关键:怎么控制在统一时间的并发量为2?
在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏)
1. 信号量Semaphore的介绍
我们以一个停车场运作为例来说明信号量的作用。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦。以后来的车必须在入口等待,直到停车场中有车辆离开。这时,如果有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开一辆,则又可以放入一辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。信号量是一个非负整数,表示了当前公共资源的可用数目(在上面的例子中可以用空闲的停车位类比信号量),当一个线程要使用公共资源时(在上面的例子中可以用车辆类比线程),首先要查看信号量,如果信号量的值大于1,则将其减1,然后去占有公共资源。如果信号量的值为0,则线程会将自己阻塞,直到有其它线程释放公共资源。
在信号量上我们定义两种操作: acquire(获取) 和 release(释放)。当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
2. 信号量Semaphore的源码分析
在Java的并发包中,Semaphore类表示信号量。Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。Semaphore有两个构造函数,参数permits表示许可数,它最后传递给了AQS的state值。线程在运行时首先获取许可,如果成功,许可数就减1,线程运行,当线程运行结束就释放许可,许可数就加1。如果许可数为0,则获取失败,线程位于AQS的等待队列中,它会被其它释放许可的线程唤醒。在创建Semaphore对象的时候还可以指定它的公平性。一般常用非公平的信号量,非公平信号量是指在获取许可时先尝试获取许可,而不必关心是否已有需要获取许可的线程位于等待队列中,如果获取失败,才会入列。而公平的信号量在获取许可时首先要查看等待队列中是否已有线程,如果有则入列。
构造函数源代码
//非公平的构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
} //通过fair参数决定公平性
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire源代码
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
} final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
可以看出,如果remaining <0 即获取许可后,许可数小于0,则获取失败,在doAcquireSharedInterruptibly方法中线程会将自身阻塞,然后入列。
release源代码
public void release() {
sync.releaseShared(1);
} public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
} protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
可以看出释放许可就是将AQS中state的值加1。然后通过doReleaseShared唤醒等待队列的第一个节点。可以看出Semaphore使用的是AQS的共享模式,等待队列中的第一个节点,如果第一个节点成功获取许可,又会唤醒下一个节点,以此类推。
3. 使用示例
package javalearning; import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreDemo {
private Semaphore smp = new Semaphore(3);
private Random rnd = new Random(); class TaskDemo implements Runnable{
private String id;
TaskDemo(String id){
this.id = id;
}
@Override
public void run(){
try {
smp.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(rnd.nextInt(1000));
smp.release();
System.out.println("Thread " + id + " is over");
} catch (InterruptedException e) {
}
}
} public static void main(String[] args){
SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
//注意我创建的线程池类型,
ExecutorService se = Executors.newCachedThreadPool();
se.submit(semaphoreDemo.new TaskDemo("a"));
se.submit(semaphoreDemo.new TaskDemo("b"));
se.submit(semaphoreDemo.new TaskDemo("c"));
se.submit(semaphoreDemo.new TaskDemo("d"));
se.submit(semaphoreDemo.new TaskDemo("e"));
se.submit(semaphoreDemo.new TaskDemo("f"));
se.shutdown();
}
}
运行结果
Thread c is working
Thread b is working
Thread a is working
Thread c is over
Thread d is working
Thread b is over
Thread e is working
Thread a is over
Thread f is working
Thread d is over
Thread e is over
Thread f is over
可以看出,最多同时有三个线程并发执行,也可以认为有三个公共资源(比如计算机的三个串口)。
java并发值多线程同步业务场景以及解决方案的更多相关文章
- java并发:线程同步机制之Volatile关键字&原子操作Atomic
volatile关键字 volatile是一个特殊的修饰符,只有成员变量才能使用它,与Synchronized及ReentrantLock等提供的互斥相比,Synchronized保证了Synchro ...
- java并发与多线程面试题与问题集合
http://www.importnew.com/12773.html https://blog.csdn.net/u011163372/article/details/73995897 ...
- Java并发编程:同步容器
Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...
- 【转】Java并发编程:同步容器
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...
- 8、Java并发编程:同步容器
Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...
- Java并发与多线程教程(2)
Java同步块 Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) ...
- Java并发与多线程教程(1)
Java并发性与多线程介绍 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务 ...
- Java并发和多线程(一)基础知识
1.java线程状态 Java中的线程可以处于下列状态之一: NEW: 至今尚未启动的线程处于这种状态. RUNNABLE: 正在 Java 虚拟机中执行的线程处于这种状态. BLOCKED: 受阻塞 ...
- Java 并发和多线程(一) Java并发性和多线程介绍[转]
作者:Jakob Jenkov 译者:Simon-SZ 校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...
随机推荐
- 常用.Net 6.0 新特性
1.nameof表达式.Nameof表达式可以直接返回对象定义的名称,比如参数.枚举.变量. 控件.属性等.可以大大减少硬编码的使用,提高程序灵活性. }, 2.字符串嵌入值($). MsgBox.S ...
- Hadoop Capacity调度器概念及配置
在Yarn框架中,调度器是一块很重要的内容.有了合适的调度规则,就可以保证多个应用可以在同一时间有条不紊的工作.最原始的调度规则就是FIFO,即按照用户提交任务的时间来决定哪个任务先执行,但是这样很可 ...
- 【Gamma】Scrum Meeting 8
前言 会议定点:大运村公寓 会议时间:2019/6/7 会议目的:分配任务,准备宣传 一.任务进度 组员 上周任务进度 下阶段任务 大娃 辅助做好引导录屏 优化辅助模型 二娃 撰写会议博客 撰写会议博 ...
- 在Github或Gitee上用hexo搭建个人博客
安装nodejs环境查看node版本node -v 安装cnpmnpm install cnpm -g --registry=https://registry.npm.taobao.org 安装 He ...
- Kubernetes之Taints与Tolerations 污点和容忍
NodeAffinity节点亲和性,是Pod上定义的一种属性,使Pod能够按我们的要求调度到某个Node上,而Taints则恰恰相反,它可以让Node拒绝运行Pod,甚至驱逐Pod. Taints(污 ...
- promise、async和await
async:async function 声明将定义一个返回 AsyncFunction 对象的异步函数.当调用一个 async 函数时,会返回一个 Promise 对象.当这个 async 函数返回 ...
- Linux 就该这么学 CH07 使用RAID和LVM磁盘阵列技术
1 RAID (独立冗余磁盘阵列) RAID 技术通过把多个硬盘设备组合成一个容量更大.安全性更好的磁盘阵列,并把数据切割成多个区段之后分别存在各个不同的物理硬盘设备上,然后利用分散读写计数来提升磁盘 ...
- cad 一个小技巧--复制视口带冻结信息
cad使用 ctrl+c 和 ctrl+v 进行跨文件复制视口的时候,会出现复制视口冻结信息丢失,因为你只选择了视口进行复制, 如果要实现带上冻结信息,那么要把含有相关图层的图元一起 ctrl+c/v ...
- Jmeter之Bean shell使用(二)(转载)
转载地址:https://www.cnblogs.com/puresoul/p/4949889.html 上一篇Jmeter之Bean shell使用(一)简单介绍了下Jmeter中的Bean she ...
- 【转】JavaScript 高性能数组去重
原文地址:https://www.cnblogs.com/wisewrong/p/9642264.html 一.测试模版 数组去重是一个老生常谈的问题,网上流传着有各种各样的解法 为了测试这些解法的性 ...