流水线并发框架架构

V1

版本1:其中定义了一个 Task 类和三个继承自 Thread 类的子类 TATBTC

  • Task 类:

    • num 是一个整数变量,用于存储任务的结果。
    • taskA()taskB()taskC() 是三个任务方法,分别模拟了一些计算和等待的操作。
  • TA、TB、TC 类:

    • 这三个类分别表示三个不同的线程,每个线程执行一组任务。
    • 每个线程接收一个 ArrayList<Task> 类型的列表作为参数,在 run() 方法中,通过迭代列表,对每个 Task 对象调用相应的任务方法。
  • Main 类:

    • main 方法中,首先创建了一个包含50个 Task 对象的列表 list
    • 创建了 TATBTC 的实例,并将 list 作为参数传递给它们。
    • 启动了三个线程,分别执行 taskAtaskBtaskC
    • 创建了一个额外的线程,该线程每秒输出部分任务的结果,以便在执行过程中观察任务的完成情况。
    • 使用 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<Task> list;
    public TA(ArrayList<Task> 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<Task> list;
    public TB(ArrayList<Task> 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<Task> list;
    public TC(ArrayList<Task> 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<Task> 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值