写作初衷:网上多线程轮流打印 ABC 的例子已经很多了,但不是自己的逻辑沉淀不出知识。
最近考虑通过实现 多线程轮流打印 ABC
, 多线程累加求和
,简单总结下并发相关的知识点
内容说明:
- 仅实现了 volatile + synchronized + wait-notify 机制,目的是尽可能从简单的逻辑过渡
- 初步尝试了代码优化,即减少代码冗余,进行代码重构
1. 重构前:多个 Runnable 实现类
package com.youga.concurrent;
class ThreadPrintA implements Runnable{
String s;
ThreadPrintA(String s){
this.s = s;
}
@Override
public void run() {
Object o = new Object();
synchronized (o){
if(!s.equals("A")){
try {
o.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.print(s + " ");
o.notify();
}
}
}
class ThreadPrintB implements Runnable{
String s;
ThreadPrintB(String s){
this.s = s;
}
@Override
public void run() {
Object o = new Object();
synchronized (o){
if(!s.equals("B")){
try {
o.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.print(s + " ");
o.notify();
}
}
}
class ThreadPrintC implements Runnable{
String s;
ThreadPrintC(String s){
this.s = s;
}
@Override
public void run() {
Object o = new Object();
synchronized (o){
if(!s.equals("C")){
try {
o.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(s);
o.notify();
}
}
}
/**
* 需求分析:现在仅实现了竞争打印,需要保证次序打印
* ① 按 A,B,C 次序打印
* ② 循环打印
*/
public class PrintAbc {
public static void main(String[] args) throws InterruptedException {
while(true){
new Thread(new ThreadPrintA("A")).start();
Thread.sleep(10);
new Thread(new ThreadPrintB("B")).start();
Thread.sleep(10);
new Thread(new ThreadPrintC("C")).start();
Thread.sleep(10);
}
}
}
tips :若 main() 函数中启动每个线程时未加延迟,则不能保证 A B C 线程是按照启动顺序执行的
基本逻辑:在每个 Runnable 实现类中判断传入参数是否为待打印字符,不是则阻塞至对应的线程打印完毕
重构逻辑:利用 ABC 三者的关系,修改字符串判断的逻辑:通过类变量动态获取当前需要打印的字符串
2. 重构后:一个 Runnable 实现类
class ThreadPrint implements Runnable{
private static volatile int cnt = 0;
private String s;
ThreadPrint(String s){
this.s = s;
}
@Override
public void run() {
Object o = new Object();
synchronized (o){
String ss = String.valueOf((char)('A' + cnt));
if(!s.equals(ss)){
try {
o.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.print(s);
o.notify();
if(cnt == 2) System.out.println();
cnt = (cnt + 1) % 3;
}
}
}
/**
* ① 按 A,B,C 次序打印
* ② 循环打印
* ③ 代码重构
*/
public class PrintAbc1 {
public static void main(String[] args) throws InterruptedException {
while(true){
for(int i = 0; i < 3; i++){
new Thread(new ThreadPrint(String.valueOf((char)('A' + i)))).start();
Thread.sleep(100);
}
}
}
}
为啥用 volatile 和 static 修饰 cnt,这就不说了,说出来的都是别人总结出来的,自己实践下最好
实践之后也不是自己的,因为还涉及到 语言特性,编译原理,CPU,指令架构…
摸清楚这些后,还不是你的,做人要诚实,但也要有所追求,认识这点的意义在于:或许有一天有些东西是属于你的
我老是觉得:从这个层面来想,学习 Java 是可以 上穷碧落下黄泉
的,这是我迄今为止依然坚持的原因,即使这是 2022 年的寒秋
反思了下:
- 博客还是要尽可能简洁
- 已有的结论尽可能少写,实验得出的结论可分析一下
- 加强实践,代码为先