【Java并发编程】01.Semaphore的使用

时间:2021-06-02 18:01:19

今天开始准备学习Java并发编程。准备把这个系列写到一个专栏里面,记录一下我学习Java并发编程的过程。希望和大家共同进步。

所有代码我会上传到GitHub上面,地址是:https://github.com/mrbcy/JavaConcurrentLearn

这个系列目前更新的部分是《Java并发编程核心方法与框架》的读书笔记。

Semaphore 的同步性

类Semaphore所提供的功能完全就是synchronized关键字的升级版,但它提供的功能更加的强大和方便,主要的作用是控制线程并发的数量。这一点单纯用synchronized是做不到的。

Semaphore发放许可的计算方式是“减法”操作。

public class Service {
private Semaphore semaphore = new Semaphore(1);

public void testMethod(){
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() +
" begin timer=" + System.currentTimeMillis());

Thread.sleep(5000);

System.out.println(Thread.currentThread().getName() +
" begin timer=" + System.currentTimeMillis());

semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Semaphore构造方法中的permit是许可的意思,代表同一时间内最多允许多少个线程同时执行acquire和release之间的代码。

acquire()方法代表使用一个许可,做减法操作。

下面我们同时启动几个线程,看一下Semaphore类是否能够控制住并发的数量。

TestThread.java

public class TestThread extends Thread{
private Service service;

public TestThread(Service service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();

service.testMethod();
}


}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_1;

public class Run {
public static void main(String[] args) {
Service service = new Service();

TestThread a = new TestThread(service);
a.setName("A");

TestThread b = new TestThread(service);
b.setName("B");

TestThread c = new TestThread(service);
c.setName("C");

a.start();
b.start();
c.start();
}
}

输出的结果为:

A begin timer=1487347454779
A begin timer=1487347459780
B begin timer=1487347459780
B begin timer=1487347464781
C begin timer=1487347464781
C begin timer=1487347469781

可以看出只有一个线程同时执行。

如果把Service类中Semaphore的permit数量改成2,则会有下面的输出结果:

B begin timer=1487347739934
A begin timer=1487347739934
B end timer=1487347744934
A end timer=1487347744934
C begin timer=1487347744934
C end timer=1487347749935

permits值如果>1,不能保证线程安全,可能出现数据脏读。

动态添加permits

不只可以调用acquire方法和release来使用/释放1个许可,它们可以传入参数,一次性使用或释放多个许可。下面的代码动态的添加了多个permits:

package tech.mrbcy.javaconcurrentlearn.e01_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();
semaphore.release();
System.out.println(semaphore.availablePermits());
semaphore.release(4);

System.out.println(semaphore.availablePermits());
} catch (Exception e) {
e.printStackTrace();
}
}
}

输出的结果是:

0
6
10

获取目前等待线程数

Service.java

package tech.mrbcy.javaconcurrentlearn.e01_3;

import java.util.concurrent.Semaphore;

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

TestThread.java

package tech.mrbcy.javaconcurrentlearn.e01_3;

public class TestThread extends Thread{
private Service service;

public TestThread(Service service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();

service.testMethod();
}


}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_3;

public class Run {
public static void main(String[] args) {
Service service = new Service();

TestThread firstThread = new TestThread(service);
firstThread.start();

TestThread[] threads = new TestThread[4];

for(int i = 0; i < 4; i++){
threads[i] = new TestThread(service);
threads[i].start();
}
}
}

输出结果为:

大约还有 4 个线程在等待
是否有线程在等待信号量?true
大约还有 3 个线程在等待
是否有线程在等待信号量?true
大约还有 2 个线程在等待
是否有线程在等待信号量?true
大约还有 1 个线程在等待
是否有线程在等待信号量?true
大约还有 0 个线程在等待
是否有线程在等待信号量?false

公平信号量与非公平信号量

公平信号量是指先启动的线程一定会先获得锁。非公平信号量则没有这个限制。下面用代码来进行一下实验。

Service.java

package tech.mrbcy.javaconcurrentlearn.e01_4;

import java.util.concurrent.Semaphore;

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

TestThread.java

package tech.mrbcy.javaconcurrentlearn.e01_4;

public class TestThread extends Thread{
private Service service;

public TestThread(Service service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();
System.out.println("ThreadName="+this.getName()+" 启动了!");
service.testMethod();
}


}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_4;

public class Run {
public static void main(String[] args) {
Service service = new Service();

TestThread firstThread = new TestThread(service);
firstThread.start();

TestThread[] threads = new TestThread[4];

for(int i = 0; i < 4; i++){
threads[i] = new TestThread(service);
threads[i].start();
}
}
}

运行结果:

ThreadName=Thread-1 启动了!
ThreadName=Thread-4 启动了!
ThreadName=Thread-0 启动了!
ThreadName=Thread-4
ThreadName=Thread-2 启动了!
ThreadName=Thread-2
ThreadName=Thread-0
ThreadName=Thread-3 启动了!
ThreadName=Thread-3
ThreadName=Thread-1

如果把Service的isFair改为true,则输出的结果变成:

ThreadName=Thread-2 启动了!
ThreadName=Thread-2
ThreadName=Thread-3 启动了!
ThreadName=Thread-3
ThreadName=Thread-0 启动了!
ThreadName=Thread-4 启动了!
ThreadName=Thread-1 启动了!
ThreadName=Thread-0
ThreadName=Thread-4
ThreadName=Thread-1

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

本实验的目标是允许多个线程同时处理任务,更具体的来说,就是每个线程都在处理自己的任务。

Service.java

package tech.mrbcy.javaconcurrentlearn.e01_5;

import java.util.concurrent.Semaphore;

public class Service {
private Semaphore semaphore = new Semaphore(3);

public void testMethod(){
try {
semaphore.acquire();
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 准备");
System.out.println("begin hello " + System.currentTimeMillis());

for(int i = 0; i < 5; i++){
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 打印" + (i+1));
}

System.out.println("end hello " + System.currentTimeMillis());
semaphore.release();
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 结束");
} catch (Exception e) {
e.printStackTrace();
} finally{

}
}
}

TestThread.java

package tech.mrbcy.javaconcurrentlearn.e01_5;

public class TestThread extends Thread{
private Service service;

public TestThread(Service service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();
service.testMethod();
}


}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_5;

public class Run {
public static void main(String[] args) {
Service service = new Service();


TestThread[] threads = new TestThread[12];

for(int i = 0; i < threads.length; i++){
threads[i] = new TestThread(service);
threads[i].start();
}
}
}

运行结果如下:

ThreadName=Thread-2 准备
ThreadName=Thread-1 准备
ThreadName=Thread-0 准备
begin hello 1487433434952
begin hello 1487433434952
ThreadName=Thread-1 打印1
begin hello 1487433434952
ThreadName=Thread-1 打印2
ThreadName=Thread-2 打印1
ThreadName=Thread-1 打印3
ThreadName=Thread-0 打印1
ThreadName=Thread-1 打印4
ThreadName=Thread-1 打印5
end hello 1487433434953
ThreadName=Thread-2 打印2
ThreadName=Thread-3 准备
begin hello 1487433434953
ThreadName=Thread-3 打印1
ThreadName=Thread-1 结束
ThreadName=Thread-0 打印2
ThreadName=Thread-3 打印2
ThreadName=Thread-2 打印3
ThreadName=Thread-3 打印3
ThreadName=Thread-3 打印4
ThreadName=Thread-0 打印3
ThreadName=Thread-3 打印5
ThreadName=Thread-2 打印4
end hello 1487433434955
ThreadName=Thread-0 打印4
ThreadName=Thread-3 结束
ThreadName=Thread-2 打印5
ThreadName=Thread-0 打印5
ThreadName=Thread-4 准备
end hello 1487433434955
ThreadName=Thread-0 结束
end hello 1487433434955
ThreadName=Thread-6 准备
ThreadName=Thread-7 准备
begin hello 1487433434956
ThreadName=Thread-7 打印1
ThreadName=Thread-7 打印2
begin hello 1487433434955
ThreadName=Thread-7 打印3
begin hello 1487433434956
ThreadName=Thread-6 打印1
ThreadName=Thread-6 打印2
ThreadName=Thread-6 打印3
ThreadName=Thread-6 打印4
ThreadName=Thread-2 结束
ThreadName=Thread-6 打印5
ThreadName=Thread-7 打印4
ThreadName=Thread-7 打印5
ThreadName=Thread-4 打印1
end hello 1487433434956
end hello 1487433434956
ThreadName=Thread-7 结束
ThreadName=Thread-4 打印2
ThreadName=Thread-8 准备
ThreadName=Thread-6 结束
begin hello 1487433434956
ThreadName=Thread-5 准备
begin hello 1487433434957
ThreadName=Thread-5 打印1
ThreadName=Thread-4 打印3
ThreadName=Thread-5 打印2
ThreadName=Thread-5 打印3
ThreadName=Thread-5 打印4
ThreadName=Thread-5 打印5
end hello 1487433434957
ThreadName=Thread-5 结束
ThreadName=Thread-8 打印1
ThreadName=Thread-9 准备
begin hello 1487433434957
ThreadName=Thread-4 打印4
ThreadName=Thread-9 打印1
ThreadName=Thread-9 打印2
ThreadName=Thread-9 打印3
ThreadName=Thread-8 打印2
ThreadName=Thread-8 打印3
ThreadName=Thread-9 打印4
ThreadName=Thread-4 打印5
ThreadName=Thread-9 打印5
ThreadName=Thread-8 打印4
ThreadName=Thread-8 打印5
end hello 1487433434957
end hello 1487433434957
ThreadName=Thread-10 准备
begin hello 1487433434957
ThreadName=Thread-10 打印1
ThreadName=Thread-10 打印2
ThreadName=Thread-9 结束
end hello 1487433434957
ThreadName=Thread-10 打印3
ThreadName=Thread-4 结束
ThreadName=Thread-11 准备
ThreadName=Thread-10 打印4
ThreadName=Thread-8 结束
ThreadName=Thread-10 打印5
end hello 1487433434959
begin hello 1487433434959
ThreadName=Thread-10 结束
ThreadName=Thread-11 打印1
ThreadName=Thread-11 打印2
ThreadName=Thread-11 打印3
ThreadName=Thread-11 打印4
ThreadName=Thread-11 打印5
end hello 1487433434960
ThreadName=Thread-11 结束

可以看出有多个线程在同时运行。

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

这个实验是在上面的多进路-多处理-多出路实验基础上稍加修改的。目标是允许多个线程同时处理任务,但执行任务的顺序缺是同步的,也就是阻塞的,所以称为单处理。

Service.java

package tech.mrbcy.javaconcurrentlearn.e01_6;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;


public class Service {
private Semaphore semaphore = new Semaphore(3);
private ReentrantLock lock = new ReentrantLock();

public void testMethod(){
try {
semaphore.acquire();
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 准备");
lock.lock();
System.out.println("begin hello " + System.currentTimeMillis());

for(int i = 0; i < 5; i++){
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 打印" + (i+1));
}

System.out.println("end hello " + System.currentTimeMillis());
lock.unlock();
semaphore.release();
System.out.println("ThreadName=" + Thread.currentThread().getName() + " 结束");
} catch (Exception e) {
e.printStackTrace();
} finally{

}
}
}

其他都没有变化。

输出结果如下:

ThreadName=Thread-1 准备
ThreadName=Thread-0 准备
ThreadName=Thread-2 准备
begin hello 1487433927769
ThreadName=Thread-1 打印1
ThreadName=Thread-1 打印2
ThreadName=Thread-1 打印3
ThreadName=Thread-1 打印4
ThreadName=Thread-1 打印5
end hello 1487433927769
ThreadName=Thread-11 准备
begin hello 1487433927769
ThreadName=Thread-1 结束
ThreadName=Thread-0 打印1
ThreadName=Thread-0 打印2
ThreadName=Thread-0 打印3
ThreadName=Thread-0 打印4
ThreadName=Thread-0 打印5
end hello 1487433927770
ThreadName=Thread-0 结束
ThreadName=Thread-3 准备
begin hello 1487433927770
ThreadName=Thread-2 打印1
ThreadName=Thread-2 打印2
ThreadName=Thread-2 打印3
ThreadName=Thread-2 打印4
ThreadName=Thread-2 打印5
end hello 1487433927771
ThreadName=Thread-2 结束
ThreadName=Thread-4 准备
begin hello 1487433927771
ThreadName=Thread-11 打印1
ThreadName=Thread-11 打印2
ThreadName=Thread-11 打印3
ThreadName=Thread-11 打印4
ThreadName=Thread-11 打印5
end hello 1487433927771
ThreadName=Thread-11 结束
ThreadName=Thread-5 准备
begin hello 1487433927771
ThreadName=Thread-3 打印1
ThreadName=Thread-3 打印2
ThreadName=Thread-3 打印3
ThreadName=Thread-3 打印4
ThreadName=Thread-3 打印5
end hello 1487433927772
ThreadName=Thread-3 结束
ThreadName=Thread-6 准备
begin hello 1487433927772
ThreadName=Thread-4 打印1
ThreadName=Thread-4 打印2
ThreadName=Thread-4 打印3
ThreadName=Thread-4 打印4
ThreadName=Thread-4 打印5
end hello 1487433927772
ThreadName=Thread-4 结束
begin hello 1487433927772
ThreadName=Thread-7 准备
ThreadName=Thread-5 打印1
ThreadName=Thread-5 打印2
ThreadName=Thread-5 打印3
ThreadName=Thread-5 打印4
ThreadName=Thread-5 打印5
end hello 1487433927772
ThreadName=Thread-5 结束
begin hello 1487433927772
ThreadName=Thread-9 准备
ThreadName=Thread-6 打印1
ThreadName=Thread-6 打印2
ThreadName=Thread-6 打印3
ThreadName=Thread-6 打印4
ThreadName=Thread-6 打印5
end hello 1487433927772
ThreadName=Thread-6 结束
ThreadName=Thread-8 准备
begin hello 1487433927773
ThreadName=Thread-7 打印1
ThreadName=Thread-7 打印2
ThreadName=Thread-7 打印3
ThreadName=Thread-7 打印4
ThreadName=Thread-7 打印5
end hello 1487433927773
ThreadName=Thread-7 结束
ThreadName=Thread-10 准备
begin hello 1487433927773
ThreadName=Thread-9 打印1
ThreadName=Thread-9 打印2
ThreadName=Thread-9 打印3
ThreadName=Thread-9 打印4
ThreadName=Thread-9 打印5
end hello 1487433927773
ThreadName=Thread-9 结束
begin hello 1487433927773
ThreadName=Thread-8 打印1
ThreadName=Thread-8 打印2
ThreadName=Thread-8 打印3
ThreadName=Thread-8 打印4
ThreadName=Thread-8 打印5
end hello 1487433927773
ThreadName=Thread-8 结束
begin hello 1487433927773
ThreadName=Thread-10 打印1
ThreadName=Thread-10 打印2
ThreadName=Thread-10 打印3
ThreadName=Thread-10 打印4
ThreadName=Thread-10 打印5
end hello 1487433927773
ThreadName=Thread-10 结束

使用Semaphore创建字符串池

这个demo涉及到了一个新的概念:Condition。详细的说明可以参考http://ifeve.com/understand-condition/

ListPool.java

package tech.mrbcy.javaconcurrentlearn.e01_7;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
* Condition 可以参考http://ifeve.com/understand-condition/
* @author mrbcy
*
*/
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(" mrbcy " + (i + 1));
}
}

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

MyThread.java

package tech.mrbcy.javaconcurrentlearn.e01_7;

public class MyThread extends Thread{
private ListPool listPool;

public MyThread(ListPool listPool) {
super();
this.listPool = listPool;
}

@Override
public void run() {
super.run();

for(int i = 0; i < Integer.MAX_VALUE; i++){
String getString = listPool.get();
System.out.println(Thread.currentThread().getName() + " 取得值 "
+ getString);
listPool.put(getString);
}
}


}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_7;

public class Run {
public static void main(String[] args) {
ListPool listPool = new ListPool();


MyThread[] threads = new MyThread[12];

for(int i = 0; i < threads.length; i++){
threads[i] = new MyThread(listPool);
threads[i].start();
}
}
}

运行结果:

Thread-9 取得值  mrbcy 2
Thread-9 取得值 mrbcy 2
Thread-9 取得值 mrbcy 2
Thread-9 取得值 mrbcy 2
Thread-9 取得值 mrbcy 2
Thread-9 取得值 mrbcy 2
Thread-10 取得值 mrbcy 2
Thread-3 取得值 mrbcy 3
Thread-10 取得值 mrbcy 2
Thread-10 取得值 mrbcy 2
Thread-10 取得值 mrbcy 2
Thread-10 取得值 mrbcy 2
Thread-10 取得值 mrbcy 2
Thread-7 取得值 mrbcy 2
Thread-4 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-9 取得值 mrbcy 1
Thread-9 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-4 取得值 mrbcy 1
Thread-10 取得值 mrbcy 1
Thread-10 取得值 mrbcy 1
Thread-9 取得值 mrbcy 1
Thread-7 取得值 mrbcy 2
Thread-4 取得值 mrbcy 2
Thread-3 取得值 mrbcy 3
Thread-10 取得值 mrbcy 3
Thread-4 取得值 mrbcy 2
Thread-7 取得值 mrbcy 2
Thread-7 取得值 mrbcy 2
Thread-3 取得值 mrbcy 2
Thread-3 取得值 mrbcy 2
Thread-4 取得值 mrbcy 2
Thread-9 取得值 mrbcy 1
Thread-9 取得值 mrbcy 1
Thread-4 取得值 mrbcy 2
Thread-10 取得值 mrbcy 3
Thread-4 取得值 mrbcy 2

Semaphore多生产者多消费者实验

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

RepastService.java

package tech.mrbcy.javaconcurrentlearn.e01_8;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


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();
// 暂存产品,只能有4个
volatile private Object[] produceShelf = new Object[4];

private boolean isEmpty() {
boolean isEmpty = true;
for(int i = 0; i < produceShelf.length; i++){
if(produceShelf[i] != null){
isEmpty = false;
break;
}
}

return isEmpty;
}

private boolean isFull() {
boolean isFull = true;
for(int i = 0; i < produceShelf.length; i++){
if(produceShelf[i] == null){
isFull = false;
break;
}
}
return isFull;
}

public void set() {
try {
setSemaphore.acquire();
lock.lock();

while(isFull()){
setCondition.await();
}

for(int i = 0; i < produceShelf.length; i++){
if(produceShelf[i] == null){
produceShelf[i] = "数据";
System.out.println(Thread.currentThread().getName()
+ " 生产了 " + produceShelf[i]);
break;
}
}

getCondition.signalAll();
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
}finally{
setSemaphore.release();
}

}

public void get() {
try {
getSemaphore.acquire();
lock.lock();
while (isEmpty()){
getCondition.await();
}

for(int i = 0; i < produceShelf.length; i++){
if(produceShelf[i] != null){
System.out.println(Thread.currentThread().getName()
+ " 消费了 " + produceShelf[i]);
produceShelf[i] = null;
break;
}
}

setCondition.signalAll();
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
} finally {
getSemaphore.release();
}
}
}

ThreadC.java

package tech.mrbcy.javaconcurrentlearn.e01_8;

public class ThreadC extends Thread{

private RepastService service;

public ThreadC(RepastService service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();
service.get();
}



}

ThreadP.java

package tech.mrbcy.javaconcurrentlearn.e01_8;

public class ThreadP extends Thread{

private RepastService service;

public ThreadP(RepastService service) {
super();
this.service = service;
}

@Override
public void run() {
super.run();
service.set();
}



}

Run.java

package tech.mrbcy.javaconcurrentlearn.e01_8;

public class Run {
public static void main(String[] args) throws InterruptedException {
RepastService service = new RepastService();

ThreadP[] threadPs = new ThreadP[60];
ThreadC[] threadCs = new ThreadC[60];

for(int i = 0; i < 60; i++){
threadPs[i] = new ThreadP(service);
threadCs[i] = new ThreadC(service);
}

Thread.sleep(2000);

for(int i = 0; i < 60; i++){
threadPs[i].start();
threadCs[i].start();
}
}
}

运行结果:

Thread-0 生产了 数据
Thread-8 生产了 数据
Thread-9 消费了 数据
Thread-1 消费了 数据
Thread-12 生产了 数据
Thread-13 消费了 数据
Thread-2 生产了 数据
Thread-18 生产了 数据
Thread-23 消费了 数据
Thread-19 消费了 数据
Thread-20 生产了 数据
Thread-3 消费了 数据
Thread-4 生产了 数据
Thread-6 生产了 数据
Thread-28 生产了 数据
Thread-5 消费了 数据
Thread-36 生产了 数据
Thread-37 消费了 数据
Thread-41 消费了 数据
Thread-11 消费了 数据
Thread-10 生产了 数据
Thread-7 消费了 数据
Thread-14 生产了 数据
Thread-16 生产了 数据
Thread-54 生产了 数据
Thread-15 消费了 数据
Thread-61 消费了 数据
Thread-63 消费了 数据
Thread-24 生产了 数据
Thread-42 生产了 数据
Thread-25 消费了 数据
Thread-67 消费了 数据
Thread-22 生产了 数据
Thread-71 消费了 数据
Thread-30 生产了 数据
Thread-73 消费了 数据
Thread-26 生产了 数据
Thread-31 消费了 数据
Thread-32 生产了 数据
Thread-33 消费了 数据
Thread-38 生产了 数据
Thread-89 消费了 数据
Thread-40 生产了 数据
Thread-39 消费了 数据
Thread-34 生产了 数据
Thread-55 消费了 数据
Thread-94 生产了 数据
Thread-100 生产了 数据
Thread-59 消费了 数据
Thread-58 生产了 数据
Thread-109 消费了 数据
Thread-60 生产了 数据
Thread-110 生产了 数据
Thread-112 生产了 数据
Thread-57 消费了 数据
Thread-44 生产了 数据
Thread-117 消费了 数据
Thread-69 消费了 数据
Thread-119 消费了 数据
Thread-87 消费了 数据
Thread-46 生产了 数据
Thread-21 消费了 数据
Thread-74 生产了 数据
Thread-68 生产了 数据
Thread-27 消费了 数据
Thread-29 消费了 数据
Thread-52 生产了 数据
Thread-75 消费了 数据
Thread-50 生产了 数据
Thread-78 生产了 数据
Thread-76 生产了 数据
Thread-80 生产了 数据
Thread-77 消费了 数据
Thread-48 生产了 数据
Thread-35 消费了 数据
Thread-56 生产了 数据
Thread-45 消费了 数据
Thread-81 消费了 数据
Thread-43 消费了 数据
Thread-53 消费了 数据
Thread-62 生产了 数据
Thread-114 生产了 数据
Thread-116 生产了 数据
Thread-64 生产了 数据
Thread-85 消费了 数据
Thread-91 消费了 数据
Thread-93 消费了 数据
Thread-65 消费了 数据
Thread-66 生产了 数据
Thread-104 生产了 数据
Thread-98 生产了 数据
Thread-95 消费了 数据
Thread-17 消费了 数据
Thread-70 生产了 数据
Thread-106 生产了 数据
Thread-97 消费了 数据
Thread-79 消费了 数据
Thread-101 消费了 数据
Thread-72 生产了 数据
Thread-103 消费了 数据
Thread-82 生产了 数据
Thread-99 消费了 数据
Thread-84 生产了 数据
Thread-105 消费了 数据
Thread-86 生产了 数据
Thread-107 消费了 数据
Thread-90 生产了 数据
Thread-88 生产了 数据
Thread-92 生产了 数据
Thread-96 生产了 数据
Thread-111 消费了 数据
Thread-113 消费了 数据
Thread-51 消费了 数据
Thread-115 消费了 数据
Thread-102 生产了 数据
Thread-108 生产了 数据
Thread-118 生产了 数据
Thread-49 消费了 数据
Thread-47 消费了 数据
Thread-83 消费了 数据

总结

这篇博客主要使用了Semaphore类提供的各种方法,还用字符串池以及生产者消费者的例子进行了综合的应用。