Semaphore和Exchanger

类Semaphore的主要作用是限制并发执行的线程数,它具有synchronized所不具有的强大功能,比如等待获取许可的同时可以加入等待时间,还有尝试是否可以持有锁等这类扩展功能,可以说它是强有力的控制并发线程个数的解决方案之一。
Exchanger是线程间传输数据的方式之一,而且在传输数据类型上没有任何限制。

Semaphore

Semaphore又称信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。

构造函数

1
public Semaphore(int permits)

其中permits指同时运行的线程数量
可通过控制构造函数的permits,控制并发执行的线程数。

单线程示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class App {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
Single single = new Single(semaphore);
for (int i = 0; i < 3; i ++) {
new TestThread("Test-" + i, single).start();;
}
}
}
class TestThread extends Thread {
private Single single;
public TestThread(String name, Single single) {
super(name);
this.single = single;
}
@Override
public void run() {
single.test();
}

}
class Single {
private Semaphore semaphore;
public Single(Semaphore semaphore) {
this.semaphore = semaphore;
}
public void test() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " start at " + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " stop at " + System.currentTimeMillis());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果

1
2
3
4
5
6
Test-0 start at 1524806263164
Test-0 stop at 1524806264166
Test-2 start at 1524806264166
Test-2 stop at 1524806265167
Test-1 start at 1524806265167
Test-1 stop at 1524806266167

方法介绍

1
2
3
4
5
6
7
8
9
10
acquire(int permits)还可以每次调用获取permits个许可。
acquireUninterruptibly()方法的作用是使进入等待许可的线程,不允许被中断。
availablePermits()返回当前可用的许可数量
drainPermits()获取并返回当前可用的许可数量,并将可用许可数置0
getQueueLength()获取等待许可的线程数
hasQueuedThreads()判断是否还有线程在等待这个许可
tryAcquire()尝试获取一个许可,无阻塞
tryAcquire(int permits)尝试获取permits个许可,无阻塞
tryAcquire(long timeout, TimeUnit unit)在指定时间内尝试获取一个许可以
tryAcquire(int permits, long timeout, TimeUnit unit)在指定时间内尝试获取permits个许可以

该类还支持公平和非公平许可:默认非公平

1
public Semaphore(int permits, boolean fair)

注:Semaphore并不能保证线程安全性,因此还是有可能会出现多个线程共同访问实例变量,导致出现脏数据的情况。

Exchanger

Exchanger的功能可以使2个线程之间传输数据,它比生产者/消费者模式产生的wait/notify要更加方便。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class App {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
Single single = new Single(exchanger);
for (int i = 0; i < 2; i ++) {
new TestThread("Test-" + i, single).start();;
}
}
}
class TestThread extends Thread {
private Single single;
public TestThread(String name, Single single) {
super(name);
this.single = single;
}
@Override
public void run() {
single.test(Thread.currentThread().getName());
}

}
class Single {
private Exchanger<String> exchanger;
public Single(Exchanger<String> exchanger) {
this.exchanger = exchanger;
}
public void test(String s) {
try {
System.out.println("send=" + s + " and receive=" + exchanger.exchange(s));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果

1
2
send=Test-0 and receive=Test-1
send=Test-1 and receive=Test-0

方法介绍

1
exchange(V x, long timeout, TimeUnit unit) 指定时间内没有其他线程获取数据,则出现超时异常