版本1:其中定义了一个 Task
类和三个继承自 Thread
类的子类 TA
、TB
和 TC
。
Task 类:
num
是一个整数变量,用于存储任务的结果。taskA()
、taskB()
和 taskC()
是三个任务方法,分别模拟了一些计算和等待的操作。TA、TB、TC 类:
ArrayList
类型的列表作为参数,在 run()
方法中,通过迭代列表,对每个 Task
对象调用相应的任务方法。Main 类:
main
方法中,首先创建了一个包含50个 Task
对象的列表 list
。TA
、TB
和 TC
的实例,并将 list
作为参数传递给它们。taskA
、taskB
和 taskC
。join()
方法等待三个线程执行完成。任务执行过程:
TA
线程每次迭代调用 taskA()
,导致 num
值增加 10。TB
线程每次迭代调用 taskB()
,导致 num
值乘以 20。TC
线程每次迭代调用 taskC()
,导致 num
值乘以自身。输出:
list
中每个第五个任务的结果。需要注意的是,由于线程之间并发执行,输出结果可能会交错。此外,对 num
的操作可能导致竞态条件,可能需要使用同步机制来确保线程安全性。
import java.util.ArrayList;
// A-B-C : 40000 正确完成任务后的结果
public class Task {
int num;
public void taskA() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num += 10;
}
public void taskB() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num *= 20;
}
public void taskC() {
try {
Thread.sleep(650);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num *= num;
}
}
class TA extends Thread {
ArrayList list;
public TA(ArrayList list) {
this.list=list;
}
@Override
public void run() {
for(int i=0;i list;
public TB(ArrayList list) {
this.list=list;
}
@Override
public void run() {
for(int i=0;i list;
public TC(ArrayList list) {
this.list=list;
}
@Override
public void run() {
for(int i=0;i list = new ArrayList<>();
for (int i = 0; i < 50; i++) {
list.add(new Task());
}
TA ta = new TA(list);
TB tb = new TB(list);
TC tc = new TC(list);
ta.start();
tb.start();
tc.start();
//监听状态线程
new Thread(){
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int size=0;
for(int i=0;i< list.size();i++){
if(i%5==0){
System.out.println(list.get(i).num);
}
}
//System.out.println(list.size());
//System.out.println("已完成"+size+"任务");
}
}
}.start();
try {
ta.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tb.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tc.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
可见性问题是多线程并发编程中常见的一个挑战,它涉及到一个线程对共享变量的修改是否能够立即被其他线程看到。在没有适当同步机制的情况下,由于每个线程有自己的工作内存,可能会导致一个线程对变量的修改对其他线程不可见。
在 Java 中,主要有两个因素导致可见性问题:
线程工作内存: 每个线程都有自己的工作内存,用于存储主内存中的变量的副本。线程在执行时,操作的是工作内存中的变量,而不是直接操作主内存中的变量。
指令重排序: 编译器和处理器为了提高执行效率,可能会对指令进行重排序。这就可能导致一些操作的执行顺序与代码中的顺序不一致。
为了解决可见性问题,Java 提供了 volatile
关键字。使用 volatile
关键字修饰的变量具有以下特性:
可见性: 当一个线程修改了 volatile
变量的值,该变量的新值会立即被写回主内存,而其他线程在读取该变量时会直接从主内存中获取最新的值。
禁止指令重排序: volatile
关键字禁止指令重排序,确保了变量的修改按照代码的顺序执行。
例
例子中演示了使用 volatile
关键字解决多线程可见性问题的情况。
volatile
关键字:
volatile
修饰的变量 flag
表示该变量是易变的,并且任何线程对它的修改都会立即反映到其他线程中。这解决了多线程之间的可见性问题,确保一个线程对该变量的修改对其他线程是可见的。main
方法:
t1
的线程,该线程在运行时将 flag
设置为 true
。flag
的值,一直等到 flag
变为 true
才输出 "T2-end"。main
方法中,通过 Thread.sleep(2000)
使得主线程休眠 2 秒,以确保 t1
线程有足够的时间来设置 flag
的值。t1
线程。输出:
T1 - start
: t1
线程开始执行,将 flag
设置为 true
。T2-start
: 另一个线程开始执行,进入循环等待,由于 flag
初始值为 false
,因此一直等待。T1 - end
: t1
线程设置 flag
为 true
。T2-end
: 循环结束,输出 "T2-end"。关于注释的说明:
// System.out.print("");
,这是一种解决可见性问题的“空循环”技巧。通过加入一个空的循环,可以迫使线程重新读取共享变量,从而解决可见性问题。在这个例子中,由于使用了 volatile
关键字,这行注释实际上不再需要,因为 volatile
本身保证了可见性。总的来说,这个例子展示了使用 volatile
关键字解决多线程可见性问题的情况,确保一个线程对共享变量的修改能够及时被其他线程感知。
public class Test {
volatile static boolean flag;// 每次使用变量时 都会重新拷贝一份副本过来
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("T1 - start");
flag = true;
System.out.println("T1 - end");
}
};
new Thread() {
@Override
public void run() {
System.out.println("T2-start");
while (!flag) {
// System.out.print("");//可见性问题
}
System.out.println("T2-end");
}
}.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t1.start();
}
}
版本2:展示了一个多线程任务的场景,其中包含了三个任务(taskA
、taskB
、taskC
),每个任务依次执行,且必须按照一定的顺序。在 Main
类的 main
方法中,创建了一个包含 100 个 Task
对象的列表,并启动了三个线程 TA
、TB
、TC
分别执行这些任务。另外,还有一个额外的线程用于监听任务完成状态。
让我们详细讲解这段代码:
Task 类:
Task
对象包含一个整数变量 num
和三个布尔标志 flagA
、flagB
、flagC
。taskA
、taskB
、taskC
方法模拟了三个任务的执行过程,每个任务在执行前会先检查前置任务是否完成(通过相应的标志)。isEnd
方法用于判断任务是否全部完成。TA、TB、TC 类:
Main 类:
Task
对象的列表 list
。TA
、TB
和 TC
的实例,并将 list
作为参数传递给它们。taskA
、taskB
和 taskC
。join
方法等待三个任务线程执行完成。输出:
任务执行过程:
该代码通过使用标志和多线程的方式模拟了任务的顺序执行,确保了每个任务在满足条件时才执行。需要注意的是,在实际生产环境中,更复杂的同步机制可能会更适用。
import java.util.ArrayList;
public class Task {
int num;
boolean flagA = false;
boolean flagB = false;
boolean flagC = false;
public void taskA() {
if (!flagA) {
sleep(40);
num += 10;
flagA = true;
}
}
public void taskB() {
if (!flagB && flagA) {
sleep(30);
num *= 20;
flagB = true;
}
}
public void taskC() {
if (flagB && !flagC) {
sleep(30);
num *= num;
flagC = true;
}
}
public boolean isEnd() {
return flagC;
}
public void sleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class TA extends Thread {
ArrayList list;
public TA(ArrayList list) {
this.list = list;
}
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TA - run" + count++);
int size = 0;
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskA();
if (task.flagA) {
size++;
}
}
System.out.println("TA - 完成了" + size);
if (size == list.size()) {
break;
}
}
}
}
class TB extends Thread {
ArrayList list;
public TB(ArrayList list) {
this.list = list;
}
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TB - run" + count++);
int size = 0;
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskB();
if (task.flagB) {
size++;
}
}
System.out.println("TB - 完成了" + size);
if (size == list.size()) {
break;
}
}
}
}
class TC extends Thread {
ArrayList list;
public TC(ArrayList list) {
this.list = list;
}
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TC - run" + count++);
int size = 0;
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskC();
if (task.flagC) {
size++;
}
}
System.out.println("TC - 完成了" + size);
if (size == list.size()) {
break;
}
}
}
}
class Main {
public static void main(String[] args) {
// 1: 定量任务
ArrayList list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(new Task());
}
TA ta = new TA(list);
TB tb = new TB(list);
TC tc = new TC(list);
ta.start();
tb.start();
tc.start();
// 监听状态线程
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int size = 0;
for (int i = 0; i < list.size(); i++) {
if (list.get(i).num == 40000) {
size++;
}
}
System.out.println(list.size());
System.out.println("已完成:" + size + "任务");
}
}
}.start();
try {
ta.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tb.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tc.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i).num);
// }
}
}
版本3:代码进一步改进了多线程任务的实现,引入了 volatile
关键字来确保线程之间的可见性。同时,通过在任务的 while
循环中进行阻塞等待,实现了任务的顺序执行。
Task 类:
volatile
关键字被添加到 flagA
、flagB
、flagC
上,确保了对这些标志的修改立即可见于其他线程。taskA
、taskB
、taskC
中的 while
循环用于在前置任务完成前阻塞当前任务的执行,确保按照任务顺序执行。TA、TB、TC 类:
while
循环进行阻塞等待前置任务完成。Main 类:
Task
对象的列表 list
。TA
、TB
和 TC
的实例,并将 list
作为参数传递给它们。taskA
、taskB
和 taskC
。join
方法等待三个任务线程执行完成。输出:
通过引入 volatile
关键字和阻塞等待的方式,确保了任务的按顺序执行,同时在输出中显示了任务执行的次数、已完成的任务数量以及整个任务执行的耗时。这种方式更加稳健地处理了多线程环境下的任务执行顺序问题。
import java.util.ArrayList;
public class Task {
int num;
volatile boolean flagA = false;
volatile boolean flagB = false;
volatile boolean flagC = false;
public void taskA() {
if (!flagA) {
sleep(40);
num += 10;
flagA = true;
}
}
public void taskB() {
while (!flagA) {// 阻塞
}
if (!flagB) {
sleep(30);
num *= 20;
flagB = true;
}
}
public void taskC() {
while (!flagB) {// 阻塞
}
if (!flagC) {
sleep(30);
num *= num;
flagC = true;
}
}
public boolean isEnd() {
return flagC;
}
public void sleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class TA extends Thread {
ArrayList list;
public TA(ArrayList list) {
this.list = list;
}
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskA();
}
}
}
class TB extends Thread {
ArrayList list;
public TB(ArrayList list) {
this.list = list;
}
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskB();
}
}
}
class TC extends Thread {
ArrayList list;
public TC(ArrayList list) {
this.list = list;
}
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
task.taskC();
}
}
}
class Main {
public static void main(String[] args) {
// 1: 定量任务
ArrayList list = new ArrayList<>();
for (int i = 0; i < 500; i++) {
list.add(new Task());
}
TA ta = new TA(list);
TB tb = new TB(list);
TC tc = new TC(list);
ta.start();
tb.start();
tc.start();
// 监听状态线程
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int size = 0;
for (int i = 0; i < list.size(); i++) {
if (list.get(i).num == 40000) {
size++;
}
}
System.out.println(list.size());
System.out.println("已完成:" + size + "任务");
}
}
}.start();
long start = System.currentTimeMillis();
try {
ta.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tb.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
tc.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).num);
}
System.out.println("耗时:" + (end - start) + " ms");
}
}
版本4:展示了一种通过使用队列(LinkedList
)和原料库(taskA
、taskB
、taskC
)的方式来实现任务的生产和消费。每个任务分别在不同的线程中执行,通过队列和原料库的操作来确保任务按照特定的顺序执行。
Task 类:
taskA
、taskB
、taskC
分别对应三个不同的任务,对 num
进行不同的操作。TA、TB、TC 类:
run
方法中使用 poll
方法从队列中取出任务,执行相应的任务方法,然后将任务放入下一个阶段的原料库中。Main 类:
taskA
、taskB
、taskC
作为任务的原料库。tasks
作为成品库,用于存储已完成的任务。TA
、TB
和 TC
的实例,并将相应的原料库和成品库传递给它们。taskA
、taskB
和 taskC
。输出:
通过这种队列和原料库的方式,确保了任务的顺序执行。任务被依次推送到不同的阶段,从而保证了任务的有序性。在这个模型中,每个任务线程负责从上一个阶段的原料库取任务,执行后将任务放入下一个阶段的原料库,以此类推,直至完成。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.ArrayBlockingQueue;
public class Task {
int num;
public void taskA() {
num += 10;
}
public void taskB() {
num *= 20;
}
public void taskC() {
num *= num;
}
}
class TA extends Thread {
LinkedList taskB;
LinkedList taskA;
public TA(LinkedList taskA, LinkedList taskB) {
this.taskA = taskA;
this.taskB = taskB;
}
@Override
public void run() {
while (true) {
Task task = taskA.poll();// 出列
if (task != null) {
task.taskA();// 执行A任务
taskB.offer(task);// 入队B
}
}
}
}
class TB extends Thread {
LinkedList taskB;
LinkedList taskC;
public TB(LinkedList taskB, LinkedList taskC) {
this.taskB = taskB;
this.taskC = taskC;
}
@Override
public void run() {
while (true) {
Task task = taskB.poll();// 出列
if (task != null) {
task.taskB();// 执行A任务
taskC.offer(task);// 入队B
}
}
}
}
class TC extends Thread {
ArrayList tasks;
LinkedList taskC;
public TC(LinkedList taskC, ArrayList tasks) {
this.taskC = taskC;
this.tasks = tasks;
}
@Override
public void run() {
while (true) {
Task task = taskC.poll();// 出列
if (task != null) {
task.taskC();// 执行A任务
tasks.add(task);// 入队B
}
}
}
}
class Main {
public static void main(String[] args) {
// 1: 定量任务
LinkedList taskA = new LinkedList<>();// A的原料库
LinkedList taskB = new LinkedList<>();// B的原料库
LinkedList taskC = new LinkedList<>();// C的原料库
ArrayList tasks = new ArrayList<>();// 成品库
ArrayBlockingQueue tasks1 = new ArrayBlockingQueue<>(50);
for (int i = 0; i < 500; i++) {
taskA.offer(new Task());// 入队
}
TA ta = new TA(taskA, taskB);
TB tb = new TB(taskB, taskC);
TC tc = new TC(taskC, tasks);
ta.start();
tb.start();
tc.start();
// 监听状态线程
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int size = 0;
for (int i = 0; i < tasks.size(); i++) {
System.out.println(tasks.get(i).num);
if (tasks.get(i).num == 40000) {
size++;
}
}
System.out.println(tasks.size());
System.out.println("已完成:" + size + "任务");
}
}
}.start();
}
}
版本5:代码是一个改进版本,使用了 ArrayBlockingQueue
作为任务的原料库和成品库,以及通过队列操作来保证任务的有序执行。
Task 类:
taskA
、taskB
、taskC
方法分别对应三个不同的任务,对 num
进行不同的操作。TA、TB、TC 类:
ArrayBlockingQueue
实现了线程之间的协作,确保任务按照特定的顺序执行。run
方法中使用 take
方法从队列中取出任务,执行相应的任务方法,然后将任务放入下一个阶段的队列中。Main 类:
taskA
、taskB
、taskC
作为任务的原料库,使用 ArrayBlockingQueue
进行初始化。tasks
作为成品库,用于存储已完成的任务。TA
、TB
和 TC
的实例,并将相应的原料库和成品库传递给它们。taskA
、taskB
和 taskC
。输出:
通过使用 ArrayBlockingQueue
作为原料库和成品库,以及使用 take
和 put
方法来保证线程之间的协作,这段代码实现了有序执行的任务模型。每个任务线程负责从上一个阶段的队列取任务,执行后将任务放入下一个阶段的队列,从而保证了任务的有序性。
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
public class Task {
int num;
public void taskA() {
num += 10;
}
public void taskB() {
num *= 20;
}
public void taskC() {
num *= num;
}
}
class TA extends Thread {
ArrayBlockingQueue taskB;
ArrayBlockingQueue taskA;
public TA(ArrayBlockingQueue taskA, ArrayBlockingQueue taskB) {
this.taskA = taskA;
this.taskB = taskB;
}
@Override
public void run() {
while (true) {
try {
Task task = taskA.take(); // 出队
task.taskA(); // 执行A任务
taskB.put(task); // 入队B
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class TB extends Thread {
ArrayBlockingQueue taskB;
ArrayBlockingQueue taskC;
public TB(ArrayBlockingQueue taskB, ArrayBlockingQueue taskC) {
this.taskB = taskB;
this.taskC = taskC;
}
@Override
public void run() {
while (true) {
try {
Task task = taskB.take(); // 出队
task.taskB(); // 执行B任务
taskC.put(task); // 入队C
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class TC extends Thread {
ArrayList tasks;
ArrayBlockingQueue taskC;
public TC(ArrayBlockingQueue taskC, ArrayList tasks) {
this.taskC = taskC;
this.tasks = tasks;
}
@Override
public void run() {
while (true) {
try {
Task task = taskC.take(); // 出队
task.taskC(); // 执行C任务
tasks.add(task); // 入队结果列表
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class Main {
public static void main(String[] args) {
// 1:定量任务
ArrayBlockingQueue taskA = new ArrayBlockingQueue<>(500); // A的原料库
ArrayBlockingQueue taskB = new ArrayBlockingQueue<>(500); // B的原料库
ArrayBlockingQueue taskC = new ArrayBlockingQueue<>(500); // C的原料库
ArrayList tasks = new ArrayList<>(); // 成品库
for (int i = 0; i < 500; i++) {
taskA.offer(new Task()); // 入队
}
TA ta = new TA(taskA, taskB);
TB tb = new TB(taskB, taskC);
TC tc = new TC(taskC, tasks);
ta.start();
tb.start();
tc.start();
// 监听线程状态
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int size = 0;
for (int i = 0; i < tasks.size(); i++) {
System.out.println(tasks.get(i).num);
if (tasks.get(i).num == 40000) {
size++;
}
}
System.out.println(tasks.size());
System.out.println("已完成:" + size + "任务");
}
}
}.start();
}
}
ArrayBlockingQueue
是 Java 中 BlockingQueue
接口的一个具体实现,它基于数组实现的有界阻塞队列。以下是对 ArrayBlockingQueue
的详细讲解:
有界队列: ArrayBlockingQueue
是一个有界队列,其容量在创建时被指定,不能动态扩展。这意味着队列中的元素数量不能超过指定的容量。
阻塞操作: 当队列满时,试图将元素放入队列的操作将被阻塞,直到队列有空间。当队列为空时,试图从队列中取出元素的操作将被阻塞,直到队列中有元素。
线程安全: ArrayBlockingQueue
提供了在多线程环境下安全使用的机制,内部实现使用了锁来保护队列的操作。
ArrayBlockingQueue(int capacity)
: 创建一个指定容量的 ArrayBlockingQueue
,默认情况下为公平策略,即等待时间最长的线程将被优先执行。
ArrayBlockingQueue(int capacity, boolean fair)
: 创建一个指定容量和公平性策略的 ArrayBlockingQueue
。
ArrayBlockingQueue(int capacity, boolean fair, Collection extends E> c)
: 创建一个包含指定集合元素的 ArrayBlockingQueue
,容量为指定容量,公平性由 fair
参数决定。
放入元素:
put(E e)
: 将指定元素放入队列,如果队列满,则阻塞直到队列有空间。
offer(E e, long timeout, TimeUnit unit)
: 将指定元素放入队列,如果队列满,则等待指定时间,超时后返回 false
。
取出元素:
take()
: 取出并删除队列的头部元素,如果队列为空,则阻塞直到队列有元素可取。
poll(long timeout, TimeUnit unit)
: 取出并删除队列的头部元素,如果队列为空,则等待指定时间,超时后返回 null
。
其他方法:
remainingCapacity()
: 返回队列的剩余容量。
size()
: 返回队列中的元素数量。
peek()
: 返回队列的头部元素,但不删除。如果队列为空,则返回 null
。
contains(Object o)
: 判断队列是否包含指定元素。