单词Semaphore的中文含义是信号、信号系统意思。此类的主要作用是限制线程并发的数量,如果不限制线程并发的数量。,则CPUD的资源很快就会被耗尽,每个线程执行的任务相当缓慢,因为CPU要把时间片分配给不同的线程对象,而且上下文切换也要耗时,最终造成系统运行效率大幅降低,所以限制并发线程的数量还是非常有必要的。
在生活中也存在这种场景,比如一个生产键盘的生产商,发布了10个代理销售许可,所有最多只有10个代理商来获得其中的一个许可,这样就限制了代理商的数量,同理也限制了线程的并发数量,这就是Semaphore类要达到的目的。
Semaphore类发放许可的计算方式是“减法”操作。
类Semaphore的同步性
多线程中的同步概念其实就是排着队去执行一个任务,执行任务是一个一个执行,并不能并行执行,这样的优点是有助于程序逻辑的正确性,不会出现非线程安全问题,保证软件系统功能上的运行稳定性。
创建实验用的项目SemaphoreTest1,类S而vice.java代码如下:
package com.yc.semephore;
import java.util.concurrent.Semaphore;
public class Service {
private Semaphore semaphore = new Semaphore(1);
public void test(){
try {
semaphore.acquire();
System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
Thread.sleep(5*1000);
System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
无参方法acquire()的作用是使用一个许可,是减法操作。
创建3个线程类如下:
ThreadA:
package com.yc.semephore;
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
this.service = service;
}
@Override
public void run() {
service.test();
}
}
ThreadB:
package com.yc.semephore;
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service){
this.service = service;
}
@Override
public void run() {
service.test();
}
}
ThreadC:
package com.yc.semephore;
public class ThreadC extends Thread{
private Service service;
public ThreadC(Service service){
this.service = service;
}
@Override
public void run() {
service.test();
}
}
运行类SemaphoreTest.java代码如下:
package com.yc.semephore;
public class SemaphoreTest {
public static void main(String[] args) {
Service service = new Service();
ThreadA ta = new ThreadA(service);
ThreadB tb = new ThreadB(service);
ThreadC tc = new ThreadC(service);
ta.setName("A");
tb.setName("B");
tc.setName("C");
ta.start();
tb.start();
tc.start();
}
}
运行结果为:
A begin time:1489640996583
A end time:1489641001583
B begin time:1489641001583
B end time:1489641006583
C begin time:1489641006583
C end time:1489641011584
private Semaphore semaphore = new Semaphore(1);
来定义最多允许1个线程执行acquire()和release()之间的代码。
类Semaphore构造方法permits参数的作用
参数permits的作用是设置许可的个数,前面已经使用过代码:
private Semaphore semaphore = new Semaphore(1);
来进行程序的设计,使同一时间内最多只有一个线程可以执行acquire()和release()之间的代码,因为只有一个许可,其实还可以传入 >1 的许可,代表同一时间内,最多允许有 x 个线程可以执行acquire()和release()之间的代码。
创建实验用的项目SemaphoreTest2,将SemaphoreTest1项目中的所有源代码复制到SemaTest2中。并该类Service.java代码如下:
package com.yc.semephore;
import java.util.concurrent.Semaphore;
public class Service {
private Semaphore semaphore = new Semaphore(2);
public void test(){
try {
semaphore.acquire();
System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
Thread.sleep(5*1000);
System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
某一次的运行结果如下:
A begin time:1489668385080
B begin time:1489668385080
A end time:1489668390081
C begin time:1489668390081
B end time:1489668390082
C end time:1489668395081
从它的输出结果来看,对Semaphore类的构造方法传递的参数permits值如果大于1时,该类并不能保证线程安全性,因为还是有可能会出现多个线程共同访问实例变量,导致出现脏数据的情况。
方法acquire(int permits)参数作用及动态添加permits许可数量
有参方法acquire(int permits)的功能就是每调用1次此方法,就使用 x 个许可
有参方法release(int permits)的功能就是每调用1次此方法,就释放 x 个许可
创建Java项目Semaphore_acquire_release,创建类Service,java代码如下:
package com.yc.semephore_1;
import java.util.concurrent.Semaphore;
public class Service {
private Semaphore semaphore = new Semaphore(10);
public void test(){
try {
semaphore.acquire(2);
System.out.println( Thread.currentThread().getName() + "\tbegin time:" + System.currentTimeMillis());
int value = (int) (Math.random() * 10000);
Thread.sleep(value);
System.out.println( Thread.currentThread().getName() + "\tsleep time:" + value);
System.out.println( Thread.currentThread().getName() + "\tend time:" + System.currentTimeMillis());
semaphore.release(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程类ThreadA的代码如下:
package com.yc.semephore_1;
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
this.service = service;
}
@Override
public void run() {
service.test();
}
}
测试类SemaphoreAcquireRelease.java的代码如下:
package com.yc.semephore_1;
public class SemaphoreAcquireReleaseTest {
public static void main(String[] args) {
Service service = new Service();
ThreadA[] threads = new ThreadA[10];
for(int i = 0; i < threads.length; i ++){
threads[i] = new ThreadA(service);
threads[i].start();
}
}
}
由上面的测试代码得到了两组结果,如下面的图一和图二
-------------
在代码中一共有10个许可,每次执行semaphore.acquire(2);代码时耗费掉 2 个,所以一共可供 10/2 = 5 个线程同时访问acquire()和release()之间的代码。 如果多次调用Semaphore类的release()或release(int )方法时,还可以动态增加permits的个数。
创建测试用的项目addPermitsCount,类Run.java代码如下:
package com.yc.semephore_2;
import java.util.concurrent.Semaphore;
public class Run {
public static void main(String[] args) {
try {
Semaphore semaphore = new Semaphore(5);
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
System.out.println(semaphore.availablePermits());
semaphore.release();
semaphore.release();
semaphore.release();
semaphore.release();
semaphore.release();
System.out.println(semaphore.availablePermits());
semaphore.release(5);
System.out.println(semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下:
0
5
10
此实验说明构造参数new Semaphore(5);中的 5 并不是左最终的许可数量,仅仅是初始的状态值。