并发技术_1_Semaphore

来源:互联网 发布:安徽管家婆软件 编辑:程序博客网 时间:2024/05/17 14:25

Semaphore


semaphore中文含义是信号、信号系统。

此类的主要作用就是限制线程并发的数量,如果不限制线程并发数量,CPU的资源很快会被耗尽,每个线程执行任务非常缓慢。

所以限制并发线程的数量是非常有必要的。

信号量在操作系统中一般用来管理数量有限的资源。信号量的值表示资源的可用数量。在使用资源时,要先从该信号量中获取一个使用许可,成功获取许可之后,信号量可用数量减1。在持有许可期间,使用者可以对资源进行操作。完成对资源的使用之后,需要释放许可,信号量可用数加1。当信号量可用数为0时,需要获取资源的线程以阻塞的方式来等待资源许可。

Semaphore:(jdk API)一个计数信号量,从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。 Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。


我个人对于semaphore的理解:

这个信号量其实就是门卫保安手里的盒子,这个盒子里面装的是许可证,这个许可证数量有限。我们操作某种资源(数据/代码)之前,必须从保安那里获取一个许可证。获取到许可证之后,盒子中许可证数量-1。在持有许可证的期间内,你可以对资源(数据/代码)进行操作。操作完之后,将许可证还到盒子中,盒子中许可证数量+1。当盒子中许可证数量为0时,后面的人全部等着,等到前面有人换许可证为止。




Semaphore的同步性

多线程中的同步概念其实就是排着队去执行一个任务,任务一个个的执行,不能并发执行,这样的优点是:有助于程序逻辑的正确性,不会出现非线程安全问题,保证系统功能上的运行稳定性。

我们来举个栗子:

public class Service {//permits参数是许可的意思,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码。private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {//获取一个许可(如果提供了一个)并立即返回,将可用的许可数减1。semaphore.acquire();System.out.println(Thread.currentThread().getName() + " begin timer = " + System.currentTimeMillis());Thread.sleep(5000);System.out.println(Thread.currentThread().getName() + " end time = " + System.currentTimeMillis());//释放一个许可,将其返回给信号量,将可用的许可数增加1。semaphore.release();} catch (Exception e) {e.printStackTrace();}}}public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadC extends Thread {private Service service;public ThreadC(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class TestThread {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");ThreadB b = new ThreadB(service);b.setName("B");ThreadC c = new ThreadC(service);c.setName("C");a.start();b.start();c.start();}}输出结果:A begin timer = 1495695149997A end time = 1495695155004B begin timer = 1495695155004B end time = 1495695160013C begin timer = 1495695160013C end time = 1495695165021
通过这个栗子,可以看出:

private Semaphore semaphore = new Semaphore(1);

最多允许1个线程执行acquire()和release()之间的代码。



Semaphore构造方法permits参数作用:

private Semaphore semaphore = new Semaphore(permits);

permits参数代表:同一时间,最多允许x个线程可以执行acquire()和release()之间的代码。

还是上面的例子,将permits参数改成2之后的结果是这样的:

B begin timer = 1495708685380A begin timer = 1495708685380A end time = 1495708690388B end time = 1495708690388C begin timer = 1495708690388C end time = 1495708695396

从结果上可以看出,同一时间有2个线程可以同时执行acquire()release()之间的代码。

不过,需要注意的是,当Semaphore构造方法permits参数大于1时,该类并不能保证线程安全性。

因为可能会出现多个线程同时访问实例变量,导致出现脏数据的情况。



方法acquire(int permits)参数作用

有参方法acquire(int permits)的功能是 每调用一次这个方法,就获取permits个许可。

(一次问门口保安,要x张许可证)

package com.concurrent.semaphore_3;import java.util.concurrent.Semaphore;public class Service {// 同一时间内,只能有10个线程同时访问private Semaphore semaphore = new Semaphore(10);public void testMethod() {try {// 获取许可semaphore.acquire(10);System.out.println(Thread.currentThread().getName() + " begin time = " + System.currentTimeMillis());int sleepValue = (int) (Math.random() * 10000);System.out.println(Thread.currentThread().getName() + " 停止了 : " + (sleepValue / 1000) + "秒");Thread.sleep(sleepValue);System.out.println(Thread.currentThread().getName() + " end time = " + System.currentTimeMillis());// 使用完成之后释放semaphore.release(10);} catch (Exception e) {e.printStackTrace();}}}package com.concurrent.semaphore_3;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}package com.concurrent.semaphore_3;public class Run {public static void main(String[] args) {Service service = new Service();ThreadA[] a = new ThreadA[10];for (int i = 0; i < 10; i++) {a[i] = new ThreadA(service);a[i].start();}}}
这个代码很有意思,一开始我没理解semaphore.acquire(10);的意义,后来想明白了。

其实和上面说的一样,一次性从保安盒子里获取10个许可,然后别人就都等着了...

一个线程可以取10个许可证,也可以取2个许可证,反正许可证就那么多,要是盒子里面没有了,其余的线程就等着吧...




方法acquireUninterruptibly()的使用

方法acquireUninterruptibly()的作用是使等待进入acquire()方法的线程,不允许被中断。


jdk API 中的解释

acquireUninterruptibly 

从信号量中获取许可,在有可用的许可前将其阻塞。 

如果当前线程在等待许可时被中断,那么它将继续等待


我们先来看一个线程能被中断的栗子:

public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " begin time = " + System.currentTimeMillis());for (int i = 0; i < Integer.MAX_VALUE / 50; i++) {String newStr = new String();Math.random();}System.out.println(Thread.currentThread().getName() + " end time = " + System.currentTimeMillis());semaphore.release();} catch (Exception e) {System.out.println("线程 : " + Thread.currentThread().getName() + " 进入了catch");e.printStackTrace();}}}public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class Run {public static void main(String[] args) {try {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();Thread.sleep(500);b.interrupt();System.out.println("main 中断了 b");} catch (Exception e) {e.printStackTrace();}}}输出结果:A begin time = 1495761247423main 中断了 b线程 : B 进入了catchjava.lang.InterruptedExceptionat java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:996)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1303)at java.util.concurrent.Semaphore.acquire(Semaphore.java:317)at com.concurrent.semaphore.semaphore_4.Service.testMethod(Service.java:13)at com.concurrent.semaphore.semaphore_4.ThreadB.run(ThreadB.java:13)A end time = 1495761248406


你看,当A获取到唯一的许可进行操作时,B线程正在等待,这时候,我将B线程中断,就会抛出这个异常。

接下来我们看看等待线程不能被中断是什么效果:

我们改造下Service类,其余的方法和上面保持一致。public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {semaphore.acquireUninterruptibly();System.out.println(Thread.currentThread().getName() + " begin time = " + System.currentTimeMillis());for (int i = 0; i < Integer.MAX_VALUE / 50; i++) {String newStr = new String();Math.random();}System.out.println(Thread.currentThread().getName() + " end time = " + System.currentTimeMillis());semaphore.release();} catch (Exception e) {System.out.println("线程 : " + Thread.currentThread().getName() + " 进入了catch");e.printStackTrace();}}}输出结果:A begin time = 1495761798442main 中断了 bA end time = 1495761799425B begin time = 1495761799425B end time = 1495761800362
从结果看,main方法执行了中断线程B的操作之后,并没有抛出任何异常。等A执行完成之后,B继续操作,完全不受main的中断操作。

acquireUninterruptibly()方法还有一个重载方法:acquireUninterruptibly(int permits)

从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。

获取permits个许可证,如果当前的线程在等待许可时被中断,则它会继续等待并且它在队列中的位置不受影响




方法availablePermits()和drainPermits()

这两个方法一般用于运维或调试,数量仅供参考,因为并不是实时数量,可能获取数量之后瞬时的又结束了一个。

availablePermits()方法:返回此信号量中当前可用的许可数。

让我们举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(10);public void testMethod() {try {System.out.println(semaphore.availablePermits());semaphore.acquire();System.out.println(semaphore.availablePermits());semaphore.release();System.out.println(semaphore.availablePermits());} catch (Exception e) {e.printStackTrace();}}}public class Run {public static void main(String[] args) {Service service = new Service();service.testMethod();}}输出结果:10910
结果显而易见...


drainPermits():获取并返回立即可用的所有许可

举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(10);public void testMethod() {try {System.out.println("当前可用许可数 : " + semaphore.availablePermits());semaphore.acquire();System.out.println("返回所有可用许可 :" + semaphore.drainPermits());System.out.println("当前可用许可数 : " + semaphore.availablePermits());semaphore.release();System.out.println("释放许可");System.out.println("当前可用许可数 : " + semaphore.availablePermits());} catch (Exception e) {e.printStackTrace();}}}public class Run {public static void main(String[] args) {Service service = new Service();service.testMethod();}}输出结果:当前可用许可数 : 10返回所有可用许可 :9当前可用许可数 : 0释放许可当前可用许可数 : 1
准确的说:drainPermits()方法获取立即可用的所有许可数量,并将可用许可置为0。





方法getQueueLength()和hashQueuedThreads()

getQueueLength():返回正在等待获取的线程的估计数目,该值仅是估计的数字,因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。

此方法用于监视系统状态,不用于同步控制。


hasQueuedThreads():查询是否有线程正在等待获取。注意,因为同时可能发生取消,所以返回 true 并不保证有其他线程等待获取许可。
此方法主要用于监视系统状态。

通常这两个方法都是判断当前有没有线程等待许可。


举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {// 获取一个许可semaphore.acquire();Thread.sleep(1000);System.out.println("大约有" + semaphore.getQueueLength() + "个线程在等待");System.out.println("是否有线程正在等待信号量?  " + semaphore.hasQueuedThreads());} catch (Exception e) {e.printStackTrace();} finally {semaphore.release();}}}public class MyThread extends Thread {private Service service;public MyThread(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class Run {public static void main(String[] args) {try {Service service = new Service();MyThread firstThread = new MyThread(service);firstThread.start();MyThread[] threadArr = new MyThread[4];for (int i = 0; i < 4; i++) {threadArr[i] = new MyThread(service);threadArr[i].start();}} catch (Exception e) {e.printStackTrace();}}}输出结果:大约有4个线程在等待是否有线程正在等待信号量?  true大约有3个线程在等待是否有线程正在等待信号量?  true大约有2个线程在等待是否有线程正在等待信号量?  true大约有1个线程在等待是否有线程正在等待信号量?  true大约有0个线程在等待是否有线程正在等待信号量?  false
从结果看,等待的线程数量随着时间的推移,逐渐递减,到最后五个线程全部执行完成,等待信号量返回false




公平与非公平信号量

有时,获得许可的顺序与线程启动的顺序有关,这时信号量就要分为公平与非公平的。

公平信号量:获得锁的顺序与线程启动的顺序有关,但不代表100%的获得信号量,仅仅是在概率上能得到保证。

非公平信号量:与线程启动顺序无关。

举个栗子:

public class Service {private boolean isFair = false;private Semaphore semaphore = new Semaphore(1, isFair);public void testMethod() {try {semaphore.acquire();System.out.println("ThreadName : " + Thread.currentThread().getName());} catch (Exception e) {e.printStackTrace();} finally {semaphore.release();}}}public class MyThread extends Thread {private Service service;public MyThread(Service service) {this.service = service;}@Overridepublic void run() {System.out.println("Thread Name : " + this.getName() + " 启动了");service.testMethod();}}public class Run {public static void main(String[] args) {Service service = new Service();MyThread firstThread = new MyThread(service);firstThread.start();MyThread[] threadArr = new MyThread[4];for (int i = 0; i < 4; i++) {threadArr[i] = new MyThread(service);threadArr[i].start();}}}输出结果:Thread Name : Thread-0 启动了Thread Name : Thread-4 启动了ThreadName : Thread-0ThreadName : Thread-4Thread Name : Thread-3 启动了ThreadName : Thread-3Thread Name : Thread-2 启动了Thread Name : Thread-1 启动了ThreadName : Thread-2ThreadName : Thread-1
输出的结果是乱序打印。

isFair = false,非公平信号量运行的效果与线程启动顺序有关,与调用semaphore.acquiire()顺序无关.也就是说线程先启动并不代表先获得许可

我们修改isFair = true。

输出结果:

Thread Name : Thread-0 启动了Thread Name : Thread-2 启动了Thread Name : Thread-4 启动了Thread Name : Thread-3 启动了Thread Name : Thread-1 启动了ThreadName : Thread-0ThreadName : Thread-2ThreadName : Thread-4ThreadName : Thread-3ThreadName : Thread-1
修改之后,有序打印。

公平信号量运行的效果 与线程启动的顺序和调用semaphore.acquire()顺序有关,也就是先启动的线程优先获得许可



方法tryAcquire

无参方法tryAcquire():

获取一个许可(如果提供了一个)并立即返回,其值为 true,将可用的许可数减 1。 

如果没有可用的许可,则此方法立即返回并且值为 false。 

----来自jdk API


尝试获取一个许可,如果获取不到则返回false,此方法通常与if语句结合使用,其具有无阻塞的特点。

这样子可以使线程不至于 一直持续等待状态,可以先做其他的事情。

举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {if (semaphore.tryAcquire()) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE / 30; i++) {String newStr = new String();Math.random();}semaphore.release();} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + "未成功进入");}}}public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class Run {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();}}输出结果:Thread Name : A 首选进入Thread Name : B未成功进入




有参tryAcquire(int permits):

获取给定数目的许可(如果提供了)并立即返回,其值为 true,将可用的许可数减去给定的量。 

如果没有足够的可用许可,则此方法立即返回,其值为 false,并且不改变可用的许可数。 

------ jdk API


有参方法tryAcquire(int permits)的作用是尝试获取X个许可,如果获取不到则返回false。

举个栗子:

其实就是上面的栗子,我们将Service修改一下public class Service {private Semaphore semaphore = new Semaphore(3);public void testMethod() {try {if (semaphore.tryAcquire(3)) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE / 50; i++) {String str = new String();Math.random();}// 上面一次性取了几个就得还几个semaphore.release(3);} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 未成功进入");}} catch (Exception e) {e.printStackTrace();}}}最后输出结果:Thread Name : A 首选进入Thread Name : B 未成功进入

在private Semaphore semaphore = new Semaphore(3);构造方法中,我们设置有3个许可证。

semaphore.tryAcquire(3)这里尝试获取三个许可证,线程A过来的时候,将3个许可证全部取走,线程B取不到,false,走分支。




有参方法tryAcquire(long timeOut,TimeUnit unit):

它的作用是在指定时间内尝试获得一个许可,如果获取不到返回false。

举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {if (semaphore.tryAcquire(10, TimeUnit.SECONDS)) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE; i++) {String str = new String();Math.random();}semaphore.release();} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 未成功进入");}} catch (Exception e) {e.printStackTrace();}}}public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class Run {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();}}输出结果:Thread Name : A 首选进入Thread Name : B 未成功进入


service类中,我们设置一共1个许可证。

线程A拿到许可之后,就没有许可证了,然后线程B就去等待,如果在10秒内还是没有许可证,就走if分支。

下面我们修改一下Service类中的参数

public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {if (semaphore.tryAcquire(100, TimeUnit.SECONDS)) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE / 20; i++) {String str = new String();Math.random();}semaphore.release();} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 未成功进入");}} catch (Exception e) {e.printStackTrace();}}}输出结果:Thread Name : A 首选进入Thread Name : B 首选进入
线程B等待100秒,如果100秒内没有许可返回,就走if分支。

我们同时修改for循环中的数量,在短时间内跑完,于是线程B顺利的拿到了许可。




有参方法tryAcquire(int permits,long timeout,TimeUnit unit):

在指定时间内尝试获取permits个许可,如果获取不到返回false。

举个栗子:

public class Service {private Semaphore semaphore = new Semaphore(3);public void testMethod() {try {if (semaphore.tryAcquire(3, 10, TimeUnit.SECONDS)) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE; i++) {String str = new String();Math.random();}// 上面获取多少,这边就要还多少semaphore.release(3);} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + "未成功进入");}} catch (Exception e) {e.printStackTrace();}}}public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}public class Run {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();}}输出结果:Thread Name : A 首选进入Thread Name : B未成功进入

线程A首先获取全部3个许可,进行操作,线程B开始等待,等待10秒,如果10秒内还是获取不到3个许可,那就进入分支。


下面我们修改一下Service类的参数:

public class Service {private Semaphore semaphore = new Semaphore(3);public void testMethod() {try {if (semaphore.tryAcquire(3, 100, TimeUnit.SECONDS)) {System.out.println("Thread Name : " + Thread.currentThread().getName() + " 首选进入");for (int i = 0; i < Integer.MAX_VALUE / 20; i++) {String str = new String();Math.random();}// 上面获取多少,这边就要还多少semaphore.release(3);} else {System.out.println("Thread Name : " + Thread.currentThread().getName() + "未成功进入");}} catch (Exception e) {e.printStackTrace();}}}输出结果:Thread Name : A 首选进入Thread Name : B 首选进入
线程A首先获取全部3个许可,进行操作,然后线程B开始等待,如果100秒内获取不到3个许可,那就进入分支。




多进路-多处理-多出路实验

Demo实验:允许多个线程同时处理任务,更具体的讲,也就是每个线程都在处理自己的任务。

public class Service {private Semaphore semaphore = new Semaphore(3);public void sayHello() {try {semaphore.acquire();System.out.println("Thread Name : " + Thread.currentThread().getName() + "准备");System.out.println("begin hello " + System.currentTimeMillis());for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "打印 " + (i + 1));}System.out.println("end time : " + System.currentTimeMillis());semaphore.release();System.out.println("Thread Name : " + Thread.currentThread().getName() + "结束");} catch (Exception e) {e.printStackTrace();}}}public class MyThread extends Thread {private Service service;public MyThread(Service service) {this.service = service;}@Overridepublic void run() {service.sayHello();}}public class Run {public static void main(String[] args) {Service service = new Service();MyThread[] threadArr = new MyThread[12];for (int i = 0; i < threadArr.length; i++) {threadArr[i] = new MyThread(service);threadArr[i].start();}}}输出结果:打印结果有点多,自己写写看,跑一下...运行的效果是多个线程同时进入,而多个线程又几乎同时执行完毕。




多进路-单处理-多出路实验

实验Demo:允许多个线程同时处理任务,但执行任务的顺序却是同步的,也就是阻塞的,也就是单处理。

public class Service {private Semaphore semaphore = new Semaphore(3);private ReentrantLock lock = new ReentrantLock();public void sayHello() {try {semaphore.acquire();System.out.println("Thread Name : " + Thread.currentThread().getName() + " 准备");lock.lock();System.out.println("begin hello " + System.currentTimeMillis());for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " 打印 " + (i + 1));}System.out.println("end hello " + System.currentTimeMillis());lock.unlock();semaphore.release();System.out.println("Thread Name : " + Thread.currentThread().getName() + " 结束");} catch (Exception e) {e.printStackTrace();}}}public class MyThread extends Thread {private Service service;public MyThread(Service service) {this.service = service;}@Overridepublic void run() {service.sayHello();}}public class Run {public static void main(String[] args) {Service service = new Service();MyThread[] threadArr = new MyThread[12];for (int i = 0; i < threadArr.length; i++) {threadArr[i] = new MyThread(service);threadArr[i].start();}}}输出结果:打印结果有点多,自己写写跑跑看;在代码中,加入了ReentrantLock对象,保证了同步性




使用Semaphore创建字符串池

Semaphore可以有效的对并发执行任务的线程数量进行限制,这种功能可以应用在pool池技术中,可以设置同时访问pool池中数据的线程数量。

实验Demo:同时有若干个线程可以访问池中的数据,但同时只有一个线程可以取得数据,使用完毕之后放回池中。

public class ListPool {private int poolMaxSize = 3;private int semaphorePermits = 5;private List<String> list = new ArrayList<String>();private Semaphore concurrencySemaphore = new Semaphore(semaphorePermits);private ReentrantLock lock = new ReentrantLock();private Condition condition = lock.newCondition();public ListPool() {for (int i = 0; i < poolMaxSize; i++) {list.add("CYX : " + i);}}public String get() {String getString = null;try {// 获取一个许可concurrencySemaphore.acquire();// 上锁lock.lock();while (list.size() == 0) {condition.await();}// 从集合中取出一个getString = list.remove(0);// 释放锁lock.unlock();} catch (Exception e) {e.printStackTrace();}return getString;}public void put(String stringValue) {lock.lock();list.add(stringValue);condition.signalAll();lock.unlock();concurrencySemaphore.release();}}public class MyThread extends Thread {private ListPool listPool;public MyThread(ListPool listPool) {this.listPool = listPool;}@Overridepublic void run() {for (int i = 0; i < Integer.MAX_VALUE; i++) {String getString = listPool.get();System.out.println(Thread.currentThread().getName() + " 取得值 : " + getString);listPool.put(getString);}}}public class Run {public static void main(String[] args) {ListPool pool = new ListPool();MyThread[] threadArr = new MyThread[12];for (int i = 0; i < 12; i++) {threadArr[i] = new MyThread(pool);}for (int i = 0; i < threadArr.length; i++) {threadArr[i].start();}}}




使用Semaphore实现多生产者/多消费模式

实验Demo不光是实现生产者和消费者模式,还要限制生产者与消费者的数量。

这样代码的复杂性就提高一些,但好在使用Semaphore类实现这个功能还比较简单。

(感觉这个栗子没太看明白)

public class RepastService {volatile private Semaphore setSemaphore = new Semaphore(10);// 厨师volatile private Semaphore getSemaphore = new Semaphore(20);// 就餐者volatile private ReentrantLock lock = new ReentrantLock();volatile private Condition setCondition = lock.newCondition();volatile private Condition getCondition = lock.newCondition();// producePosition变量的含义是最多只有4个盒子存放菜品volatile private Object[] producePosition = new Object[4];private boolean isEmpty() {boolean isEmpty = true;for (int i = 0; i < producePosition.length; i++) {if (producePosition[i] != null) {isEmpty = false;break;}}if (isEmpty == true) {return true;} else {return false;}}private boolean isFull() {boolean isFull = true;for (int i = 0; i < producePosition.length; i++) {if (producePosition[i] == null) {isFull = false;break;}}return isFull;}public void set() {try {// 允许最多10个厨师进行生产setSemaphore.acquire();lock.lock();while (isFull()) {setCondition.await();}for (int i = 0; i < producePosition.length; i++) {if (producePosition[i] == null) {producePosition[i] = "数据";System.out.println(Thread.currentThread().getName() + " 生产了 " + producePosition[i]);break;}}getCondition.signalAll();lock.unlock();} catch (Exception e) {e.printStackTrace();} finally {setSemaphore.release();}}public void get() {try {getSemaphore.acquire();// 允许同时最多20个就餐者lock.lock();while (isEmpty()) {getCondition.await();}for (int i = 0; i < producePosition.length; i++) {if (producePosition[i] != null) {System.out.println(Thread.currentThread().getName() + " 消费了 " + producePosition[i]);producePosition[i] = null;break;}}setCondition.signalAll();lock.unlock();} catch (Exception e) {e.printStackTrace();} finally {getSemaphore.release();}}}public class ThreadP extends Thread {private RepastService repastService;public ThreadP(RepastService repastService) {this.repastService = repastService;}@Overridepublic void run() {repastService.set();}}public class ThreadC extends Thread {private RepastService repastService;public ThreadC(RepastService repastService) {this.repastService = repastService;}@Overridepublic void run() {repastService.get();}}public class Run {public static void main(String[] args) throws Exception {RepastService service = new RepastService();ThreadP[] arrayP = new ThreadP[60];ThreadC[] arrayC = new ThreadC[60];for (int i = 0; i < 60; i++) {arrayP[i] = new ThreadP(service);arrayC[i] = new ThreadC(service);}Thread.sleep(2000);for (int i = 0; i < 60; i++) {arrayP[i].start();arrayC[i].start();}}}输出结果就不贴了,自己跑一下...
类Semaphore提供了限制并发线程数的功能,此功能在默认的synchronized中是不提供的。

原创粉丝点击