Semaphore
semaphore中文含义是信号、信号系统。
此类的主要作用就是限制线程并发的数量,如果不限制线程并发数量,CPU的资源很快会被耗尽,每个线程执行任务非常缓慢。
所以限制并发线程的数量是非常有必要的。
信号量在操作系统中一般用来管理数量有限的资源。信号量的值表示资源的可用数量。
在使用资源时,要先从该信号量中获取一个使用许可,
成功获取许可之后,信号量可用数量减1。
在持有许可期间,使用者可以对资源进行操作。
完成对资源的使用之后,需要释放许可,信号量可用数加1。
当信号量可用数为0时,需要获取资源的线程以阻塞的方式来等待资源许可。
Semaphore:(jdk API)
一个计数信号量,从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。
每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
我个人对于semaphore的理解:
这个信号量其实就是门卫保安手里的盒子,这个盒子里面装的是许可证,这个许可证数量有限。
我们操作某种资源(数据/代码)之前,必须从保安那里获取一个许可证。
获取到许可证之后,盒子中许可证数量-1。
在持有许可证的期间内,你可以对资源(数据/代码)进行操作。
操作完之后,将许可证还到盒子中,盒子中许可证数量+1。
当盒子中许可证数量为0时,后面的人全部等着,等到前面有人换许可证为止。
多线程中的同步概念其实就是排着队去执行一个任务,任务一个个的执行,不能并发执行,这样的优点是:有助于程序逻辑的正确性,不会出现非线程安全问题,保证系统功能上的运行稳定性。
我们来举个栗子:
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;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadC extends Thread {
private Service service;
public ThreadC(Service service) {
this.service = service;
}
@Override
public 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 = 1495695149997
A end time = 1495695155004
B begin timer = 1495695155004
B end time = 1495695160013
C begin timer = 1495695160013
C 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 = 1495708685380
A begin timer = 1495708685380
A end time = 1495708690388
B end time = 1495708690388
C begin timer = 1495708690388
C 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;
}
@Override
public 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;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public 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 = 1495761247423
main 中断了 b
线程 : B 进入了catch
java.lang.InterruptedException
at 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
接下来我们看看等待线程不能被中断是什么效果:
我们改造下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 = 1495761798442
main 中断了 b
A end time = 1495761799425
B begin time = 1495761799425
B 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();
}
}
输出结果:
10
9
10
结果显而易见...
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;
}
@Override
public 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;
}
@Override
public 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-0
ThreadName : Thread-4
Thread Name : Thread-3 启动了
ThreadName : Thread-3
Thread Name : Thread-2 启动了
Thread Name : Thread-1 启动了
ThreadName : Thread-2
ThreadName : 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-0
ThreadName : Thread-2
ThreadName : Thread-4
ThreadName : Thread-3
ThreadName : 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;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public 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未成功进入
获取给定数目的许可(如果提供了)并立即返回,其值为 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;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public 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拿到许可之后,就没有许可证了,然后线程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;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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可以有效的对并发执行任务的线程数量进行限制,这种功能可以应用在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;
}
@Override
public 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();
}
}
}
实验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;
}
@Override
public void run() {
repastService.set();
}
}
public class ThreadC extends Thread {
private RepastService repastService;
public ThreadC(RepastService repastService) {
this.repastService = repastService;
}
@Override
public 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中是不提供的。