Java实现流水线工厂(1)

假设在流水线工厂中,生产一个产品需要依次执行三个任务,如何设计这样的流水线完成一批产品的生产?

我们可以启动三个线程,分别执行这三个任务。

第一步:定义产品类(任务类Task),同时定义方法Task1~3,指定每个任务做什么。

public class Task {

    public int num;

    public void task1() {
        num = 20;
    }

    public void task2() {
        num *= 10;
    }

    public void task3() {
        num *= num;
    }

}

第二步:定义三个线程Thread1~3,调取数组中的任务对象并执行任务。

public class Thread1 extends Thread{
    
    Task[] tasks;

    public Thread1(Task[] tasks) {
        this.tasks = tasks;
    }

    @Override
    public void run() {
        for (Task task : tasks) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            task.task1();
        }
    }

}
public class Thread2 extends Thread{

    Task[] tasks;

    public Thread2(Task[] tasks) {
        this.tasks = tasks;
    }

    @Override
    public void run() {
        for (Task task : tasks) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            task.task2();
        }
    }

}
public class Thread3 extends Thread{

    Task[] tasks;

    public Thread3(Task[] tasks) {
        this.tasks = tasks;
    }

    @Override
    public void run() {
        for (Task task : tasks) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            task.task3();
        }
    }

}

第三步:在主线程中,先后启动线程Thread1~3,测试一下。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Task[] tasks = new Task[20];
        for (int i = 0; i < tasks.length; i++) {
            tasks[i] = new Task();
        }
        Thread1 t1 = new Thread1(tasks);
        Thread2 t2 = new Thread2(tasks);
        Thread3 t3 = new Thread3(tasks);
        t1.start();
        t2.start();
        t3.start();
//        join: 等待线程运行结束
        t1.join();
        t2.join();
        t3.join();
        for (Task task : tasks) {
            System.out.println(task.num);
        }
    }
}

测试结果(按照顺序依次执行三个任务的正确结果应为40000):

40000
0
4000
400
200
40000
0
0
20
20
40000
4000
0
0
20
400
0
200
40000
40000


问题:为什么出现了错误的结果?

原因:并不是哪个线程先启动,哪个线程就先执行。虽然线程按照1~3的顺序先后启动,但是某些产品也可能出现后续任务先被执行的情况。

解决:面对这个问题,我们做了如下尝试。

第一次尝试:加启动间隔,两个线程之间间隔1000ms。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Task[] tasks = new Task[20];
        for (int i = 0; i < tasks.length; i++) {
            tasks[i] = new Task();
        }
        Thread1 t1 = new Thread1(tasks);
        Thread2 t2 = new Thread2(tasks);
        Thread3 t3 = new Thread3(tasks);
        t1.start();
        Thread.sleep(1000);
        t2.start();
        Thread.sleep(1000);
        t3.start();
//        join: 等待线程运行结束
        t1.join();
        t2.join();
        t3.join();
        for (Task task : tasks) {
            System.out.println(task.num);
        }
    }
}

测试结果:每个结果都是40000。

但这样的解决方案仍然存在缺陷。如果生产的是成千上万个任务,1000ms的间隔就不够用了,为了得到正确结果我们不得不设置更长的间隔,从而导致效率低下。

第二次尝试:完全不考虑任务执行的先后顺序,而是在执行完任务后贴上相应的标签——后序的任务,如果遇到没有贴上代表前序任务已经完成的标签的产品,就不执行本次任务,到下一次遍历时再检查标签情况。用这样的方式可以实现任务线程之间的信息交流。

public class Task {

    public int num;

    boolean flag1 = false;
    boolean flag2 = false;
    boolean flag3 = false;

    public void task1() {
        if (!flag1) {
            num = 20;
            flag1 = true;
        }
    }

    public void task2() {
        if (flag1 && !flag2) {
            num *= 10;
            flag2 = true;
        }
    }

    public void task3() {
        if (flag1 && flag2 && !flag3) {
            num *= num;
            flag3 = true;
        }
    }

}

在任务线程Thread1中,反复遍历产品数组,直到所有产品完成Task1为止。(2、3同理)

public class Thread1 extends Thread {

    Task[] tasks;

    public Thread1(Task[] tasks) {
        this.tasks = tasks;
    }

    @Override
    public void run() {
        while (true) {
            int count = 0;
            for (Task task : tasks) {
                task.task1();
                if (task.flag1) {
                    count++;
                }
            }
            if (count == 20) {
                break;
            }
        }
    }

}

测试结果:每个结果都是40000。

但这样的做法依然存在缺陷,有些任务可能需要遍历数组很多次。我们用下面的代码测试一下。

我们再设置一个变量,用于记录所在任务遍历数组的次数。

    @Override
    public void run() {
        int times = 0;
        while (true) {
            times++;
            int count = 0;
            for (Task task : tasks) {
                task.task1();
                if (task.flag1) {
                    count++;
                }
            }
            if (count == 5000) {
                break;
            }
        }
        System.out.println("Task1 - " + times);
    }

测试结果:

Task1 - 1
Task2 - 2
Task3 - 2

这表明Task2和Task3都遍历了两次才完成任务。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值