并发技术_1_Semaphore

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;
	}

	@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
	


你看,当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 = 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未成功进入
	




有参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;
	}

	@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 未成功进入
	


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;
	}

	@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创建字符串池

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();
	}

	}

}




使用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;
	}

	@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中是不提供的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值