concurrent 工具类

#CountDownLatch

阻塞直到数完为止

比如说下班锁门,必须要等屋子里所有人都走了之后,才能锁门。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xyz.onns.juc;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(9);
for (int i = 0; i < 9; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " count down!");
countDownLatch.countDown();
}, String.valueOf(i + 1)).start();
}

countDownLatch.await();

System.out.println("Done");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
$ /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57648:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/onns/Documents/code/java/jvm/out/production/jvm xyz.onns.juc.CountDownLatchTest
1 count down!
2 count down!
3 count down!
4 count down!
5 count down!
6 count down!
7 count down!
8 count down!
9 count down!
Done

Process finished with exit code 0

假如上面的i变成10,第十个人是否会“被锁在里面”是不固定的。

#CyclicBarrier

等待前置条件全部满足之后,才会执行后面的操作

必须要集齐七颗龙珠,才能召唤神龙 🐲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package xyz.onns.juc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙!");
});
for (int i = 0; i < 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集第" + (i + 1) + "个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "线程" + String.valueOf(i + 1)).start();
}
}
}

这样写,第十三行会报错:

Variable used in lambda expression should be final or effectively final

查了一下,首先,这样做的原因是,因为 Java 是这样规定的:Lambda Expressions

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

至于为什么会这样规定,参照这里:Why are only final variables accessible in anonymous class?

When you create an instance of an anonymous inner class, any variables which are used within that class have their values copied in via the autogenerated constructor. This avoids the compiler having to autogenerate various extra types to hold the logical state of the “local variables”, as for example the C# compiler does… (When C# captures a variable in an anonymous function, it really captures the variable - the closure can update the variable in a way which is seen by the main body of the method, and vice versa.)
As the value has been copied into the instance of the anonymous inner class, it would look odd if the variable could be modified by the rest of the method - you could have code which appeared to be working with an out-of-date variable (because that’s effectively what would be happening… you’d be working with a copy taken at a different time). Likewise if you could make changes within the anonymous inner class, developers might expect those changes to be visible within the body of the enclosing method.
Making the variable final removes all these possibilities - as the value can’t be changed at all, you don’t need to worry about whether such changes will be visible. The only ways to allow the method and the anonymous inner class see each other’s changes is to use a mutable type of some description. This could be the enclosing class itself, an array, a mutable wrapper type… anything like that. Basically it’s a bit like communicating between one method and another: changes made to the parameters of one method aren’t seen by its caller, but changes made to the objects referred to by the parameters are seen.

简单来说,实现可修改的变量功能所带来便利,没有随之而来的问题多。

解决办法就是,加一个final变量,其实从Java 1.8之后,你不加final关键字,只要这个值没被修改,也会默认视作final的。

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
package xyz.onns.juc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙!");
});
for (int i = 0; i < 7; i++) {
int finalI = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集第" + (finalI + 1) + "个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "线程" + String.valueOf(i + 1)).start();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
$ /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57758:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/onns/Documents/code/java/jvm/out/production/jvm xyz.onns.juc.CyclicBarrierTest
线程4收集第4个龙珠
线程5收集第5个龙珠
线程7收集第7个龙珠
线程6收集第6个龙珠
线程2收集第2个龙珠
线程3收集第3个龙珠
线程1收集第1个龙珠
召唤神龙!

Process finished with exit code 0

#相关链接

#Semaphore

信号量

抢车位,有空车位才能停车

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
package xyz.onns.juc;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 9; i++) {
int finalI = i % 3 + 1;
new Thread(() -> {
try {
semaphore.acquire(finalI);
System.out.println(Thread.currentThread().getName() + "获得了" + finalI + "个车位");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
for (int j = 0; j < finalI; j++) {
semaphore.release();
System.out.println(Thread.currentThread().getName() + "释放了第" + (j + 1) + "个车位");
}
}
}, String.valueOf(i + 1)).start();
}
}
}
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
$ /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=58074:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/onns/Documents/code/java/jvm/out/production/jvm xyz.onns.juc.SemaphoreTest
1获得了1个车位
2获得了2个车位
1释放了第1个车位
2释放了第1个车位
2释放了第2个车位
3获得了3个车位
4获得了1个车位
3释放了第1个车位
3释放了第2个车位
3释放了第3个车位
5获得了2个车位
5释放了第1个车位
4释放了第1个车位
6获得了3个车位
5释放了第2个车位
6释放了第1个车位
6释放了第2个车位
7获得了1个车位
6释放了第3个车位
8获得了2个车位
8释放了第1个车位
8释放了第2个车位
7释放了第1个车位
9获得了3个车位
9释放了第1个车位
9释放了第2个车位
9释放了第3个车位

Process finished with exit code 0

在教程的基础上改变了些,发现出 bug 了= =

出现问题

找了半个多小时,解决了!

释放车位输出到控制台,不应该在在释放之后,这样可能会,释放了但是来不及打印,那边线程就已经拿到车位了。

释放车位 -> 获得车位 -> 打印输出

释放车位 -> 打印输出 -> 获得车位

第一步之后,另外的两步是没有办法保证顺序的!所以改为:

打印输出 -> 释放车位 -> 获得车位

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
package xyz.onns.juc;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 9; i++) {
int finalI = i % 3 + 1;
new Thread(() -> {
try {
semaphore.acquire(finalI);
System.out.println("线程" + Thread.currentThread().getName() + "获得了" + finalI + "个车位");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
for (int j = 0; j < finalI; j++) {
System.out.println("线程" + Thread.currentThread().getName() + "释放第" + (j + 1) + "个车位");
semaphore.release();
}
}
}, String.valueOf(i + 1)).start();
}
}
}
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
$ /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=58503:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/onns/Documents/code/java/jvm/out/production/jvm xyz.onns.juc.SemaphoreTest
线程1获得了1个车位
线程2获得了2个车位
线程1释放第1个车位
线程2释放第1个车位
线程2释放第2个车位
线程3获得了3个车位
线程3释放第1个车位
线程3释放第2个车位
线程4获得了1个车位
线程3释放第3个车位
线程5获得了2个车位
线程4释放第1个车位
线程5释放第1个车位
线程5释放第2个车位
线程6获得了3个车位
线程6释放第1个车位
线程6释放第2个车位
线程7获得了1个车位
线程6释放第3个车位
线程8获得了2个车位
线程8释放第1个车位
线程7释放第1个车位
线程8释放第2个车位
线程9获得了3个车位
线程9释放第1个车位
线程9释放第2个车位
线程9释放第3个车位

Process finished with exit code 0

完。