CountDownLatch
//: concurrency/CountDownLatchDemo.java
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
// Performs some portion of a task:
class TaskPortion implements Runnable {
private static int counter = 0;
private final int id = counter++;
private static Random rand = new Random(47);
private final CountDownLatch latch;
TaskPortion(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
doWork();
latch.countDown(); //此线程负责调用countDown
} catch(InterruptedException ex) {
// Acceptable way to exit
}
}
public void doWork() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));
print(this + "completed");
}
public String toString() {
return String.format("%1$-3d ", id);
}
}
// Waits on the CountDownLatch:
class WaitingTask implements Runnable {
private static int counter = 0;
private final int id = counter++;
private final CountDownLatch latch;
WaitingTask(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
print("before wait" + this);
latch.await(); //此线程负责调用await
print("Latch barrier passed for " + this);
} catch(InterruptedException ex) {
print(this + " interrupted");
}
}
public String toString() {
return String.format("WaitingTask %1$-3d ", id);
}
}
public class CountDownLatchDemo {
static final int SIZE = 100;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
// All must share a single CountDownLatch object:
CountDownLatch latch = new CountDownLatch(SIZE);
for(int i = 0; i < 10; i++)
exec.execute(new WaitingTask(latch));
for(int i = 0; i < SIZE; i++)
exec.execute(new TaskPortion(latch));
print("Launched all tasks");
exec.shutdown(); // Quit when all tasks complete
}
} /* (Execute to see output) *///:~
从运行结果可以看出:
- WaitingTask的10个线程,开始都阻塞在了await处。
- 直到TaskPortion的100个线程都执行了countDown,WaitingTask的10个线程才从await处重新唤醒。
- WaitingTask的10个线程,阻塞在await处的顺序,和它们被重新唤醒的顺序,是不一样的。
CyclicBarrier
//: concurrency/HorseRace.java
// Using CyclicBarriers.
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
class Horse implements Runnable {
private static int counter = 0;
private final int id = counter++;
private int strides = 0; //记录自己这匹马从起点跑了多远
private static Random rand = new Random(47);
private static CyclicBarrier barrier;
public Horse(CyclicBarrier b) { barrier = b; }
public synchronized int getStrides() { return strides; }
public void run() {
try {
while(!Thread.interrupted()) {
synchronized(this) {
strides += rand.nextInt(3); // 每次await之前,向前随机跑0-2步
}
barrier.await();
}
} catch(InterruptedException e) {
// A legitimate way to exit
} catch(BrokenBarrierException e) {
// This one we want to know about
throw new RuntimeException(e);
}
}
public String toString() { return "Horse " + id + " "; }
public String tracks() { //打印此匹马已跑过的路程,一个*代表一步
StringBuilder s = new StringBuilder();
for(int i = 0; i < getStrides(); i++)
s.append("*");
s.append(id);
return s.toString();
}
}
public class HorseRace {
static final int FINISH_LINE = 75;
private List<Horse> horses = new ArrayList<Horse>();
private ExecutorService exec =
Executors.newCachedThreadPool();
private CyclicBarrier barrier;
public HorseRace(int nHorses, final int pause) {
barrier = new CyclicBarrier(nHorses, new Runnable() { //匿名线程内部类
public void run() {
StringBuilder s = new StringBuilder();
for(int i = 0; i < FINISH_LINE; i++)
s.append("="); // The fence on the racetrack
print(s);
for(Horse horse : horses)
print(horse.tracks()); //打印每匹马的路程
for(Horse horse : horses)
if(horse.getStrides() >= FINISH_LINE) { //如果有一匹马已经胜出(没考虑多匹马同时破线的情况)
print(horse + "won!");
exec.shutdownNow();
return;
}
try {
TimeUnit.MILLISECONDS.sleep(pause);
} catch(InterruptedException e) {
print("barrier-action sleep interrupted");
}
}
});
for(int i = 0; i < nHorses; i++) {
Horse horse = new Horse(barrier);
horses.add(horse);
exec.execute(horse);
}
}
public static void main(String[] args) {
int nHorses = 7;
int pause = 200;
if(args.length > 0) { // Optional argument
int n = new Integer(args[0]);
nHorses = n > 0 ? n : nHorses;
}
if(args.length > 1) { // Optional argument
int p = new Integer(args[1]);
pause = p > -1 ? p : pause;
}
new HorseRace(nHorses, pause);
}
} /* (Execute to see output) *///:~
- 这个demo还是比较有意思的,就好比你看赛马比赛,但你只能通过一个幻灯片的方式来看,即每隔200ms来看这一帧里每匹马的当前路程。
- 每个Horse线程都会阻塞在await处,但每阻塞一次 CyclicBarrier对象的计数器都会减少1,当7个Horse线程都执行了await,这个计数器便减小到了0。然后会执行CyclicBarrier构造器中的匿名线程,当匿名线程执行完毕后,7个Horse线程才会重新从await处唤醒。同样,阻塞的顺序和唤醒的顺序不一样。执行下面代码以观察效果:
//修改Horse的run函数:
public void run() {
try {
synchronized(this) {
strides += rand.nextInt(3); // 每次await之前,向前随机跑0-2步
}
print(id +" before await");
barrier.await();
print(id +" after await");
} catch(InterruptedException e) {
// A legitimate way to exit
} catch(BrokenBarrierException e) {
// This one we want to know about
throw new RuntimeException(e);
}
}
/*output:
0 before await
2 before await
1 before await
3 before await
5 before await
4 before await
6 before await
===========================================================================
**0
*1
**2
**3
**4
*5
*6
6 after await
2 after await
0 after await
4 after await
5 after await
3 after await
1 after await
*/
exec.shutdownNow();
必须执行,不然7个Horse线程会不停运行下去(因为while循环)。
DelayQueue
其实Java编程思想给的这个例子不好,在此之前我先讲一个简单例子帮忙理解。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
class Item implements Delayed{
private long time; //存,啥时候延时到期
String name;
public Item(String name, long time, TimeUnit unit) {
this.name = name;
this.time = System.currentTimeMillis() + (time > 0? unit.toMillis(time): 0);
}
@Override
public long getDelay(TimeUnit unit) {
return time - System.currentTimeMillis();
}
@Override
public int compareTo(Delayed o) {
Item item = (Item) o;
long diff = this.time - item.time;
if (diff <= 0) {// 改成>=会造成问题
return -1;
}else {
return 1;
}
}
}
public class DelayedQueneTest {
public static void main(String[] args) throws InterruptedException {
Item item1 = new Item("item1", 3, TimeUnit.SECONDS);
Item item2 = new Item("item2", 6, TimeUnit.SECONDS);
Item item3 = new Item("item3", 9, TimeUnit.SECONDS);
DelayQueue<Item> queue = new DelayQueue<>();
queue.put(item1);
queue.put(item2);
queue.put(item3);
System.out.println("begin time:" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
for (int i = 0; i < 3; i++) {
Item take = queue.take();
System.out.format("name:{%s}, time:{%s}\n",take.name, LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
}
}
}
/*output:
begin time:2020-05-06T22:24:12.638
name:{item1}, time:{2020-05-06T22:24:15.582}
name:{item2}, time:{2020-05-06T22:24:18.582}
name:{item3}, time:{2020-05-06T22:24:21.582}
*/
- getDelay函数,以判断当前Delayed对象是否达到延时时间。如果该函数返回小于0的值,代表延时时间还没有到达。观察getDelay的实现,它与currentTime进行比较,很明显,当时间流逝后,该函数的返回值会由负值 -> 0 -> 正值。该函数作为取出队首元素的标准,即返回值>=0时,才可以取出。
- compareTo函数,作为比较的标准。现有两个Delayed对象,如果
A对象.compareTo(B对象) < 0
,那么在DelayQueue队列中,A对象会排到B对象的前面。(A对象更靠近队列头部) - 观察打印效果,每隔3秒出来一行字。说明
queue.take();
确实会阻塞,直到队首元素的延时时间到达。 diff <= 0
如果改成diff >= 0
,那么打印结果如下。本来,打印效果是每隔3秒出来一行字,现在改完最后三行字是一起出来的,而且顺序也是不对的(变成321的顺序了)。- 变成321的顺序是因为:改成
diff >= 0
后,延时时间点更靠后的Delayed对象反而更加靠近队首,自然第一个取出来的是item3。 - 最后三行字是一起出来是因为:队列从头部到尾部的元素分别为item3、item2、item1,虽然item1最先到达延时时间,但由于队首是item3,item3还没有到达延时时间,自然不能取出队首元素。随着时间流逝,当item3到达时,item2和item1早就已经到达了,所以它们三个可以连续地取出,因为此时它们三个元素都满足取出条件。
- 变成321的顺序是因为:改成
begin time:2020-05-06T22:34:54.443
name:{item3}, time:{2020-05-06T22:35:03.381}
name:{item2}, time:{2020-05-06T22:35:03.388}
name:{item1}, time:{2020-05-06T22:35:03.388}
//: concurrency/DelayQueueDemo.java
import java.util.concurrent.*;
import java.util.*;
import static java.util.concurrent.TimeUnit.*;
import static net.mindview.util.Print.*;
class DelayedTask implements Delayed {
private static int counter = 0;
private final int id = counter++;
private final int delta; //要延时的时间
private final long trigger; //根据当前时间+delta
protected static List<DelayedTask> sequence =
new ArrayList<DelayedTask>();
public DelayedTask(int delayInMilliseconds) {
delta = delayInMilliseconds;
trigger = System.nanoTime() +
NANOSECONDS.convert(delta, MILLISECONDS);
sequence.add(this);
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(
trigger - System.nanoTime(), NANOSECONDS);
}
@Override
public int compareTo(Delayed arg) {
DelayedTask that = (DelayedTask)arg;
if(trigger < that.trigger) return -1; //返回负数,则排在队列前面
if(trigger > that.trigger) return 1;
return 0;
}
public void taskToDo() { printnb(this + " "); }
public String toString() {
return String.format("[%1$-4d]", delta) +
" Task " + id;
}
public String summary() {
return "(" + id + ":" + delta + ")";
}
public static class EndSentinel extends DelayedTask {
private ExecutorService exec;
public EndSentinel(int delay, ExecutorService e) {
super(delay);
exec = e;
}
public void taskToDo() {
for(DelayedTask pt : sequence) {
printnb(pt.summary() + " ");
}
print();
print(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}
class DelayedTaskConsumer implements Runnable {
private DelayQueue<DelayedTask> q;
public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
this.q = q;
}
public void run() {
try {
while(!Thread.interrupted())
q.take().taskToDo(); // Run task with the current thread
} catch(InterruptedException e) {
// Acceptable way to exit
System.out.println("InterruptedException");
}
print("Finished DelayedTaskConsumer");
}
}
public class DelayQueueDemo {
public static void main(String[] args) {
Random rand = new Random(47);
ExecutorService exec = Executors.newCachedThreadPool();
DelayQueue<DelayedTask> queue =
new DelayQueue<DelayedTask>();
// Fill with tasks that have random delays:
for(int i = 0; i < 20; i++)
queue.put(new DelayedTask(rand.nextInt(5000)));
// Set the stopping point
queue.add(new DelayedTask.EndSentinel(5000, exec));
exec.execute(new DelayedTaskConsumer(queue));
System.out.println("start all task");
}
} /* Output:
start all task
[128 ] Task 11 [200 ] Task 7 [429 ] Task 5 [520 ] Task 18 [555 ] Task 1 [961 ] Task 4 [998 ] Task 16 [1207] Task 9 [1693] Task 2 [1809] Task 14 [1861] Task 3 [2278] Task 15 [3288] Task 10 [3551] Task 12 [4258] Task 0 [4258] Task 19 [4522] Task 8 [4589] Task 13 [4861] Task 17 [4868] Task 6 (0:4258) (1:555) (2:1693) (3:1861) (4:961) (5:429) (6:4868) (7:200) (8:4522) (9:1207) (10:3288) (11:128) (12:3551) (13:4589) (14:1809) (15:2278) (16:998) (17:4861) (18:520) (19:4258) (20:5000)
[5000] Task 20 Calling shutdownNow()
Finished DelayedTaskConsumer
*///:~
- 此例子我进行了大改造,主要是让DelayedTask不再实现Runnable接口,把run函数的逻辑写在自定义函数taskToDo里面,之前那么搞,反而让这个例子不好理解。
unit.convert(trigger - System.nanoTime(), NANOSECONDS)
这句体现了一种策略模式,根据unit这个TimeUnit对象,和传入参数的TimeUnit对象,这二者最终确定了一个时间单位的转换关系。[128 ] Task 11 [200 ] Task 7 [429 ] Task 5 ...
,体现了,触发时间早的,排在了队列的头部。他们的依次有间隔的打印,体现了队首元素在时间未到时,会一直阻塞。
PriorityBlockingQueue
//: concurrency/PriorityBlockingQueueDemo.java
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
class PrioritizedTask implements Comparable<PrioritizedTask> {
private Random rand = new Random(47);
private static int counter = 0;
private final int id = counter++;
private final int priority;
protected static List<PrioritizedTask> sequence =
new ArrayList<PrioritizedTask>();
public PrioritizedTask(int priority) {
this.priority = priority;
sequence.add(this);
}
public int compareTo(PrioritizedTask arg) {
return priority < arg.priority ? 1 ://返回正数,放在队列后面(priority越小,位置更靠近队尾)
(priority > arg.priority ? -1 : 0);
}
public void taskToDo() {
try {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(250));
} catch(InterruptedException e) {
// Acceptable way to exit
}
print(this);
}
public String toString() {
return String.format("[%1$-3d]", priority) +
" Task " + id;
}
public String summary() {
return "(" + id + ":" + priority + ")";
}
public static class EndSentinel extends PrioritizedTask {
private ExecutorService exec;
public EndSentinel(ExecutorService e) {
super(-1); // Lowest priority in this program
exec = e;
}
public void taskToDo() {
int count = 0;
for(PrioritizedTask pt : sequence) {
printnb(pt.summary());
if(++count % 5 == 0)
print();
}
print();
print(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}
class PrioritizedTaskProducer implements Runnable {
private Random rand = new Random(47);
private Queue<PrioritizedTask> queue;
private ExecutorService exec;
public PrioritizedTaskProducer(
Queue<PrioritizedTask> q, ExecutorService e) {
queue = q;
exec = e; // Used for EndSentinel
}
public void run() {
print("生产者线程开始");
// Unbounded queue; never blocks.
// Fill it up fast with random priorities:
for(int i = 0; i < 20; i++) {
queue.add(new PrioritizedTask(rand.nextInt(10)));
Thread.yield();
}
System.out.println("接下来加入权限为10");
// Trickle in highest-priority jobs:
try {
for(int i = 0; i < 10; i++) {
TimeUnit.MILLISECONDS.sleep(250);
queue.add(new PrioritizedTask(10));
}
System.out.println("从低到高添加权限");
// Add jobs, lowest priority first:
for(int i = 0; i < 10; i++)
queue.add(new PrioritizedTask(i));
// A sentinel to stop all the tasks:
queue.add(new PrioritizedTask.EndSentinel(exec));
} catch(InterruptedException e) {
// Acceptable way to exit
}
print("Finished PrioritizedTaskProducer");
}
}
class PrioritizedTaskConsumer implements Runnable {
private PriorityBlockingQueue<PrioritizedTask> q;
public PrioritizedTaskConsumer(
PriorityBlockingQueue<PrioritizedTask> q) {
this.q = q;
}
public void run() {
print("消费者线程开始");
try {
while(!Thread.interrupted()) {
print("消费者线程 before take. q is "+q);
q.take().taskToDo();
}
} catch(InterruptedException e) {
// Acceptable way to exit
}
print("Finished PrioritizedTaskConsumer");
}
}
public class PriorityBlockingQueueDemo {
public static void main(String[] args) throws Exception {
Random rand = new Random(47);
ExecutorService exec = Executors.newCachedThreadPool();
PriorityBlockingQueue<PrioritizedTask> queue =
new PriorityBlockingQueue<PrioritizedTask>();
exec.execute(new PrioritizedTaskProducer(queue, exec));
exec.execute(new PrioritizedTaskConsumer(queue));
}
} /* (Execute to see output) *///:~
- PriorityBlockingQueue,看名字,就知道:它是一个阻塞队列,当队列无元素且想取出元素时,会阻塞;它还是一个优先队列,内部使用二叉堆。
- 同样,对此demo进行了改造,且加了打印(主要是不让PrioritizedTask实现Runnable接口了,因为这样没有意义)。
- 看源码可知,加入元素和取出元素都是对同一把锁进行加锁,自然,两个线程同时进行操作,有一个线程会因获得不到锁而阻塞。
生产者线程开始
消费者线程开始
消费者线程 before take. q is []
接下来加入权限为10
[9 ] Task 5
消费者线程 before take. q is [[9 ] Task 13, [8 ] Task 10, [9 ] Task 14, [8 ] Task 15, [7 ] Task 9, [8 ] Task 11, [8 ] Task 6, [8 ] Task 16, [8 ] Task 19, [1 ] Task 4, [5 ] Task 1, [3 ] Task 2, [1 ] Task 12, [2 ] Task 8, [8 ] Task 0, [0 ] Task 7, [1 ] Task 17, [0 ] Task 18, [1 ] Task 3]
[9 ] Task 13
...
- 从上面打印结果来看,消费者线程执行到
消费者线程 before take. q is []
,就因为没有元素而阻塞了。之后生产者连续加入了20个随机权限的元素,在这期间,虽然队列有元素了,但消费者因一直没有获得到锁而阻塞。直到生产者第一次执行到了TimeUnit.MILLISECONDS.sleep(250);
,消费者才抓住了机会,之后消费者一直取出元素直到取完(这就是为什么,打印效果是消费者线程 before take. q is []
之后是[9 ] Task 5
)。
...
消费者线程 before take. q is [[9 ] Task 13, [8 ] Task 10, [9 ] Task 14, [8 ] Task 15, [7 ] Task 9, [8 ] Task 11, [8 ] Task 6, [8 ] Task 16, [8 ] Task 19, [1 ] Task 4, [5 ] Task 1, [3 ] Task 2, [1 ] Task 12, [2 ] Task 8, [8 ] Task 0, [0 ] Task 7, [1 ] Task 17, [0 ] Task 18, [1 ] Task 3]
[9 ] Task 13
消费者线程 before take. q is [[9 ] Task 14, [8 ] Task 10, [8 ] Task 11, [8 ] Task 15, [7 ] Task 9, [3 ] Task 2, [8 ] Task 6, [8 ] Task 16, [8 ] Task 19, [1 ] Task 4, [5 ] Task 1, [1 ] Task 3, [1 ] Task 12, [2 ] Task 8, [8 ] Task 0, [0 ] Task 7, [1 ] Task 17, [0 ] Task 18]
[9 ] Task 14
...
- 从上面可见,取出Task 13后,元素的顺序就全变了,但至少还保持着队首元素为最大权限元素。这是因为二叉堆在取出元素后,有元素下滤的过程,所以是不保证顺序的。
...
消费者线程 before take. q is []
[10 ] Task 20
消费者线程 before take. q is []
[10 ] Task 21
消费者线程 before take. q is []
[10 ] Task 22
消费者线程 before take. q is []
...
- 有一段时间是生产者生产一个,消费者再消费一个的交替过程。因为生产者每次生产后,都会sleep一下。
ScheduledThreadPoolExecutor
import java.util.concurrent.*;
import java.util.*;
public class GreenhouseScheduler {
private volatile boolean light = false;
private volatile boolean water = false;
private String thermostat = "Day";
public synchronized String getThermostat() {
return thermostat;
}
public synchronized void setThermostat(String value) {
thermostat = value;
}
ScheduledThreadPoolExecutor scheduler =
new ScheduledThreadPoolExecutor(10);
public void schedule(Runnable event, long delay) {
scheduler.schedule(event,delay,TimeUnit.MILLISECONDS);
}
public void
repeat(Runnable event, long initialDelay, long period) {
scheduler.scheduleAtFixedRate(
event, initialDelay, period, TimeUnit.MILLISECONDS);
}
class LightOn implements Runnable {
public void run() {
// Put hardware control code here to
// physically turn on the light.
System.out.println("Turning on lights");
light = true;
}
}
class LightOff implements Runnable {
public void run() {
// Put hardware control code here to
// physically turn off the light.
System.out.println("Turning off lights");
light = false;
}
}
class WaterOn implements Runnable {
public void run() {
// Put hardware control code here.
System.out.println("Turning greenhouse water on");
water = true;
}
}
class WaterOff implements Runnable {
public void run() {
// Put hardware control code here.
System.out.println("Turning greenhouse water off");
water = false;
}
}
class ThermostatNight implements Runnable {
public void run() {
// Put hardware control code here.
System.out.println("Thermostat to night setting");
setThermostat("Night");
}
}
class ThermostatDay implements Runnable {
public void run() {
// Put hardware control code here.
System.out.println("Thermostat to day setting");
setThermostat("Day");
}
}
class Bell implements Runnable {
public void run() { System.out.println("Bing!"); }
}
class Terminate implements Runnable {
public void run() {
System.out.println("Terminating");
scheduler.shutdownNow();
// Must start a separate task to do this job,
// since the scheduler has been shut down:
new Thread() {
public void run() {
for(DataPoint d : data)
System.out.println(d);
}
}.start();
}
}
// New feature: data collection
static class DataPoint {
final Calendar time;
final float temperature;
final float humidity;
public DataPoint(Calendar d, float temp, float hum) {
time = d;
temperature = temp;
humidity = hum;
}
public String toString() {
return time.getTime() +
String.format(
" temperature: %1$.1f humidity: %2$.2f",
temperature, humidity);
}
}
private Calendar lastTime = Calendar.getInstance();
{ // Adjust date to the half hour
lastTime.set(Calendar.MINUTE, 30);
lastTime.set(Calendar.SECOND, 00);
}
private float lastTemp = 65.0f;
private int tempDirection = +1;
private float lastHumidity = 50.0f;
private int humidityDirection = +1;
private Random rand = new Random(47);
List<DataPoint> data = Collections.synchronizedList(
new ArrayList<DataPoint>());
class CollectData implements Runnable {
public void run() {
System.out.println("Collecting data");
synchronized(GreenhouseScheduler.this) {
// Pretend the interval is longer than it is:
lastTime.set(Calendar.MINUTE,
lastTime.get(Calendar.MINUTE) + 30);
// One in 5 chances of reversing the direction:
if(rand.nextInt(5) == 4)
tempDirection = -tempDirection;
// Store previous value:
lastTemp = lastTemp +
tempDirection * (1.0f + rand.nextFloat());
if(rand.nextInt(5) == 4)
humidityDirection = -humidityDirection;
lastHumidity = lastHumidity +
humidityDirection * rand.nextFloat();
// Calendar must be cloned, otherwise all
// DataPoints hold references to the same lastTime.
// For a basic object like Calendar, clone() is OK.
data.add(new DataPoint((Calendar)lastTime.clone(),
lastTemp, lastHumidity));
}
}
}
public static void main(String[] args) {
GreenhouseScheduler gh = new GreenhouseScheduler();
gh.schedule(gh.new Terminate(), 5000);
// Former "Restart" class not necessary:
gh.repeat(gh.new Bell(), 0, 1000);
gh.repeat(gh.new ThermostatNight(), 0, 2000);
gh.repeat(gh.new LightOn(), 0, 200);
gh.repeat(gh.new LightOff(), 0, 400);
gh.repeat(gh.new WaterOn(), 0, 600);
gh.repeat(gh.new WaterOff(), 0, 800);
gh.repeat(gh.new ThermostatDay(), 0, 1400);
gh.repeat(gh.new CollectData(), 500, 500);
}
} /* (Execute to see output) *///:~
- 好像没什么好讲的,就是schedule方法延时运行一次,scheduleAtFixedRate方法延时运行一次,之后每隔固定时间再运行。
- 注意CollectData是一个成员内部类,且run方法里锁的是GreenhouseScheduler.this,之所以不直接锁自己,是因为run方法修改的变量都是GreenhouseScheduler外部类的成员变量,这样写才显得合理。当创建了两个CollectData对象作为线程运行,或者GreenhouseScheduler有一个synchronized方法也修改了同样的成员变量时,
synchronized(GreenhouseScheduler.this)
这样写才是正确的写法。 List<DataPoint> data = Collections.synchronizedList(new ArrayList<DataPoint>());
这里通过synchronizedList静态方法获得了包装过的同步List,虽然只有一个CollectData线程会调用到它的方法。
Semaphore
//: concurrency/Pool.java
// Using a Semaphore inside a Pool, to restrict
// the number of tasks that can use a resource.
import java.util.concurrent.*;
import java.util.*;
public class Pool<T> {
private int size;
private List<T> items = new ArrayList<T>();
private volatile boolean[] checkedOut;
private Semaphore available;
public Pool(Class<T> classObject, int size) {
this.size = size;
checkedOut = new boolean[size];
available = new Semaphore(size, true);
// Load pool with objects that can be checked out:
for(int i = 0; i < size; ++i)
try {
// Assumes a default constructor:
items.add(classObject.newInstance());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
//对于管理者,把对象给出去了,所以是out
public T checkOut() throws InterruptedException {
available.acquire();
return getItem();
}
public void checkIn(T x) {
if(releaseItem(x))
available.release();
}
private synchronized T getItem() {
for(int i = 0; i < size; ++i) //从前往后找到一个没被使用的item
if(!checkedOut[i]) {
checkedOut[i] = true;
return items.get(i);
}
return null; // Semaphore prevents reaching here
}
private synchronized boolean releaseItem(T item) {
int index = items.indexOf(item);
if(index == -1) return false; // 保证item是在list里的
if(checkedOut[index]) { // 这个item现在正在被使用
checkedOut[index] = false;
return true;
}
return false; // Wasn't checked out
}
} ///:~
//: concurrency/Fat.java
// Objects that are expensive to create.
public class Fat {
private volatile double d; // Prevent optimization
private static int counter = 0;
private final int id = counter++;
public Fat() {
// Expensive, interruptible operation:
for(int i = 1; i < 10000; i++) {
d += (Math.PI + Math.E) / (double)i;
}
}
public void operation() { System.out.println(this); }
public String toString() { return "Fat id: " + id; }
} ///:~
//: concurrency/SemaphoreDemo.java
// Testing the Pool class
import java.util.concurrent.*;
import java.util.*;
import static net.mindview.util.Print.*;
// A task to check a resource out of a pool:
class CheckoutTask<T> implements Runnable {
private static int counter = 0;
private final int id = counter++;
private Pool<T> pool;
public CheckoutTask(Pool<T> pool) {
this.pool = pool;
}
public void run() {
try {
T item = pool.checkOut();
print(this + "after checked out " + item);
TimeUnit.SECONDS.sleep(1);
print(this +"before checking in " + item);
pool.checkIn(item);
} catch(InterruptedException e) {
// Acceptable way to terminate
}
}
public String toString() {
return "CheckoutTask " + id + " ";
}
}
public class SemaphoreDemo {
final static int SIZE = 25;
public static void main(String[] args) throws Exception {
final Pool<Fat> pool =
new Pool<Fat>(Fat.class, SIZE);
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < SIZE; i++)
exec.execute(new CheckoutTask<Fat>(pool));
print("All CheckoutTasks created");
List<Fat> list = new ArrayList<Fat>();
for(int i = 0; i < SIZE; i++) {
Fat f = pool.checkOut();
printnb(i + ":after main() thread checked out ");
f.operation();
list.add(f);
}
print("主线程已经将信号量占满了");
Future<?> blocked = exec.submit(new Runnable() {
public void run() {
try {
// Semaphore prevents additional checkout,
// so call is blocked:
print("这里将一直阻塞");
pool.checkOut();
} catch(InterruptedException e) {
print("checkOut() Interrupted");
}
}
});
TimeUnit.SECONDS.sleep(2);
blocked.cancel(true); // Break out of blocked call
print("Checking in objects in " + list);
for(Fat f : list)
pool.checkIn(f);
for(Fat f : list)
pool.checkIn(f); // Second checkIn ignored
exec.shutdown();
}
} /* (Execute to see output) *///:~
- 首先是运行25个CheckoutTask线程,它们会先执行acquire,再release。即取走的信号量最终会归还。
- 然后是主线程执行25次acquire,此时信号量已经被全部取走了。
- 最后执行那个匿名线程将一直阻塞,因为没有信号量可以取了。
TimeUnit.SECONDS.sleep(2);
pool.checkIn(list.get(0)); //加一句 以release一个信号量
blocked.cancel(true); // Break out of blocked call
print("Checking in objects in " + list);
for(Fat f : list)
pool.checkIn(f);
for(Fat f : list)
pool.checkIn(f); // Second checkIn ignored
exec.shutdown();
- 如果按如上代码加一句,那么最后那个匿名线程就不会一直阻塞,因为有一个信号量被释放了。
如果线程生前获得了一个信号量,随着线程的死亡,这个信号量会回收吗?为了验证这个问题,我写了如下demo:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class test5 {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
Semaphore available = new Semaphore(1);
new Thread(new Runnable() {
public void run() {
try {
available.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
available.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行后,程序没有结束,dump threads后:
"main" #1 prio=5 os_prio=0 tid=0x0000000003842800 nid=0x49fc waiting on condition [0x00000000036cf000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000740ba2088> (a java.util.concurrent.Semaphore$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)
at test5.main(test5.java:27)
- 匿名线程在执行完run函数后,就死亡了,但它没有释放信号量。
- 主线程,由于获得不到信号量,就阻塞了。
Exchanger
Exchanger就好比 两两就消去的消消乐,只不过它加了一个规则就是两两配对成功后,会将信息交换后返回给对方。
import java.util.concurrent.Exchanger;
class ExchangerTest extends Thread {
private Exchanger<String> exchanger;
private String string;
private String threadName;
public ExchangerTest(Exchanger<String> exchanger, String string,
String threadName) {
this.exchanger = exchanger;
this.string = string;
this.threadName = threadName;
}
public void run() {
try {
System.out.println(threadName + ": " + exchanger.exchange(string));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class test {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
ExchangerTest test1 = new ExchangerTest(exchanger, "string1",
"thread-1");
ExchangerTest test2 = new ExchangerTest(exchanger, "string2",
"thread-2");
ExchangerTest test3 = new ExchangerTest(exchanger, "string3", "thread-3");
ExchangerTest test4 = new ExchangerTest(exchanger, "string4", "thread-4");
test1.start();
test2.start();
test3.start();
test4.start();
}
}/*output:
thread-1: string2
thread-2: string1
*/
如果增加到三个线程,再加下面代码(注意,执行效果是随机的)。程序不会停止。
ExchangerTest test3 = new ExchangerTest(exchanger, "string3", "thread-3");
test3.start();
/*output:
thread-1: string2
thread-2: string1
*/
如果增加到四个线程,再加下面代码(注意,执行效果是随机的)。程序会停止。
ExchangerTest test3 = new ExchangerTest(exchanger, "string3", "thread-3");
ExchangerTest test4 = new ExchangerTest(exchanger, "string4", "thread-4");
test3.start();
test4.start();
/*output:
thread-1: string2
thread-4: string3
thread-3: string4
thread-2: string1
*/
综上可知:
- Exchanger只适用于两个线程之间使用。当第一个线程调用exchange时,将阻塞;当第二个线程调用exchange时,不阻塞,并且第一个线程会被唤醒。
- 当同时三个线程使用时,第三个线程由于没有第四个线程来执行exchange从而唤醒它,所以会一直阻塞,程序不停止。
- 当同时四个线程使用时,程序能够停止,因为调用exchange的次数是偶数。但交换数据的双方是随机的。
- 可以这么理解:Exchanger相当于CyclicBarrier ,只不过第一个参数,永远是2;第二个参数的匿名线程的逻辑就是交换两个元素。
//: concurrency/ExchangerDemo.java
import java.util.concurrent.*;
import java.util.*;
import net.mindview.util.*;
class ExchangerProducer<T> implements Runnable {
private Generator<T> generator; //生产者用Generator生产
private Exchanger<List<T>> exchanger;
private List<T> holder;
ExchangerProducer(Exchanger<List<T>> exchg,
Generator<T> gen, List<T> holder) {
exchanger = exchg;
generator = gen;
this.holder = holder;
}
public void run() {
try {
while(!Thread.interrupted()) {
for(int i = 0; i < ExchangerDemo.size; i++)
holder.add(generator.next());
// 每次都将充满的list去交换
holder = exchanger.exchange(holder);
}
} catch(InterruptedException e) {
// OK to terminate this way.
}
}
}
class ExchangerConsumer<T> implements Runnable {
private Exchanger<List<T>> exchanger;
private List<T> holder; //这里都没有初始化,所以默认是null
private volatile T value;
ExchangerConsumer(Exchanger<List<T>> ex, List<T> holder){
exchanger = ex;
this.holder = holder;
}
public void run() {
try {
while(!Thread.interrupted()) {
holder = exchanger.exchange(holder); //消费者不需要提供什么,所以交换null过去就行
for(T x : holder) {
value = x; // 消费list中每个元素
holder.remove(x); // CopyOnWriteArrayList可以在遍历中删除
}
}
} catch(InterruptedException e) {
// OK to terminate this way.
}
System.out.println("Final value: " + value);
}
}
public class ExchangerDemo {
static int size = 10;
static int delay = 5; // Seconds
public static void main(String[] args) throws Exception {
if(args.length > 0)
size = new Integer(args[0]);
if(args.length > 1)
delay = new Integer(args[1]);
ExecutorService exec = Executors.newCachedThreadPool();
Exchanger<List<Fat>> xc = new Exchanger<List<Fat>>();
List<Fat>
producerList = new CopyOnWriteArrayList<Fat>(),
consumerList = new CopyOnWriteArrayList<Fat>();
exec.execute(new ExchangerProducer<Fat>(xc,
BasicGenerator.create(Fat.class), producerList));
exec.execute(
new ExchangerConsumer<Fat>(xc,consumerList));
TimeUnit.SECONDS.sleep(delay);
exec.shutdownNow();
}
} /* Output: (Sample)
Final value: Fat id: 29999 (执行结果每次随机)
*///:~