多线程实战(一)——多线程轮流调用

师傅留了一个作业让我们来熟悉多线程问题,原本对多线程一直处于理论阶段,大二学操作系统的时候写的也是一知半解,今天拿到这道题又好好的做了一遍。

题目:审核系统有一批工单需要处理,现在启动三个线程进行处理,要求线程1处理工单id mod 3 = 1的工单,线程2处理工单id mod 3 = 2的工单,线程3处理工单id mod 3 = 0的工单,直到工单全部处理完毕,假设工单有1000个,工单编号从1-1000,工单处理过程简化为打印工单id,要求工单必须按顺序处理,即打印结果必须保证从1-1000从小到大递增

1、请使用原始synchronized,wait(),notify(),notifyAll()等方式来实现。

2、使用JDK1.5并发包提供的Lock,Condition等类的相关方法来实现。

对于第一个问题,网上有很多相关的代码,有重写三个run方法的,甚至有创建1000个进程的,很多都不是特别的好。受到一篇博文的启发@zyplus,我写出了下面的代码,优势之处在于传参的话只需要重写一个run方法,代码也相对优美一些。具体思想是这样的,其实我们要做的知识对3个线程的循环调用,为了控制三个线程的前后顺序,需要定义两个锁,一个是prev,即前一个线程持有的对象锁,第二个是self,用来使下一个线程进行等待和唤醒状态转换。

public class GongdanHandler extends Thread {

    private String name;
    private Object prev;
    private Object self;
    private static int id = 1;

    private GongdanHandler(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }

    public void run() {
        while (id <= 1000) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.println(name + "处理工单" + id);
                    id++;

                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public static void main(String[] args) throws Exception {

        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        GongdanHandler ta = new GongdanHandler("线程1", c, a);
        GongdanHandler tb = new GongdanHandler("线程2", a, b);
        GongdanHandler tc = new GongdanHandler("线程3", b, c);

        ta.start();
        tb.start();
        tc.start();
    }
}
对于第二个问题,就有点头疼了,看了不少对于Lock和Condition的介绍和代码,感觉还是有点懵逼,对于Condition,作为新手的我看了这些方法

await(): 使当前线程在接到信号或被中断之前处于等待状态。

signal(): 唤醒一个等待线程。

signalAll(): 唤醒所有的等待线程。

这样的话,Condition承担的工作其实就是代替了被锁锁住的对象,使它的使用更加灵活且更加好理解一些了。

本来我还是想把代码写成和上一个代码相同的思想的,这样会大大的简化代码,但是过程中发现,在线程中需要使用到Condition对象,觉得传的参数有点小多,就多重写几个run吧(在写这段话的时候我把这两份代码又进行了一次比较,觉得还是第一份代码写得好,尝试着把第二份代码也像上一个那样去写,虽然好像传的参数有点多,出了点小bug,公司没大有人了,先把这篇先写完吧)

package test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by bjhl on 16/8/1.
 */
public class GongdanHandler2 extends Thread{

    static class NumberWrapper {
        public static int value = 1;
    }

    public static void main(String[] args) {

        //初始化可重入锁
        final Lock lock = new ReentrantLock();

        final Condition reachOneCondition = lock.newCondition();
        final Condition reachTwoCondition = lock.newCondition();
        final Condition reachThreeCondition = lock.newCondition();

        final NumberWrapper num = new NumberWrapper();

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                while (num.value <= 1000) {
                    try {
                        lock.lock();
                        System.out.println("t1等待");
                        if(num.value != 1)
                            reachOneCondition.await();
                        if(num.value > 1000) break;
                        if (num.value % 3 == 1) {
                            System.out.println("t1处理:" + num.value);
                            num.value++;
                        }
                        reachTwoCondition.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                    }
                }
            }

        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                while (num.value <= 1000) {
                    try {
                        lock.lock();
                        System.out.println("t2等待");
                        reachTwoCondition.await();
                        if(num.value > 1000) break;
                        if (num.value % 3 == 2) {
                            System.out.println("t2处理:" + num.value);
                            num.value++;
                        }
                        reachThreeCondition.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }

        });
        Thread t3 = new Thread(new Runnable() {
            public void run() {
                while (num.value <= 1000) {
                    try {
                        lock.lock();
                        System.out.println("t3等待");
                        reachThreeCondition.await();
                        if(num.value > 1000) break;
                        if (num.value % 3 == 0) {
                            System.out.println("t3处理:" + num.value);
                            num.value++;
                        }
                        reachOneCondition.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }

        });

        t3.start();
        t2.start();
        t1.start();

    }


}
确实不大好看呀,这个代码也是受到一个博文的启发,一开始我的运行结果是这样的

t1等待
t1处理:1
t1等待
t2等待
t3等待

死锁,为什么会发生这种情况,因为在t1中执行reachTwoCondition.signal();的时候t2中的reachTwoCondition还没进入等待状态,我学习的那篇博文的博主教导我们把线程的顺序倒过来start不就行了么,一开始我觉得挺有道理的,也像他那样做了,运行一下,也挺成功的,后来有运行了几次,发现成功到1000是个概率事件,经常会在500多呀800多呀甚至30多发生死锁。为什么,因为循环一定的次数之后总会有概率发生之前这样t1中执行reachTwoCondition.signal();的时候t2中的reachTwoCondition还没进入等待状态,t2t3同样状态的情况,这就很尴尬了,我目前的解决办法是

<span style="white-space:pre">	</span>reachTwoCondition.await();
        Thread.sleep(10);//每个线程中加了这么一句
        if(num.value > 1000) break;
即在等待被唤醒后先等上一小段时间,但这确实不是一个非常好的办法,我再继续想办法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Delphi是一门古老而优秀的编程语言,在多线程编程方面也有一些特殊的处理方式。多线程在开发中是必备的知识,但相对来说也是比较难的知识点。 多线程可以将一个进程划分为多个线程,每个线程轮流占用CPU运行时间和资源。在多线程中,可以通过优先级管理来使重要的程序优先操作,同时也提高了任务管理的灵活性。另外,在多CPU系统中,不同的线程可以在不同的CPU中执行,从而实现真正的多任务处理。 在Delphi中,使用TThread类来实现多线程编程。TThread类是Delphi中用于创建和管理线程的基本类,它提供了一些方法和属性来控制线程的执行流程。我们可以继承TThread类,重写Execute方法来编写自己的多线程任务逻辑。 下面是一个简单的例子来说明如何在Delphi中使用多线程: ```delphi unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMyThread = class(TThread) protected procedure Execute; override; end; TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TMyThread.Execute; begin // 在这里编写你的多线程任务逻辑 Sleep(500); Synchronize(procedure begin Form1.Memo1.Lines.Add('Hello from thread!'); end); end; procedure TForm1.Button1Click(Sender: TObject); var MyThread: TMyThread; begin MyThread := TMyThread.Create(True); // 创建一个线程对象 MyThread.FreeOnTerminate := True; // 设置线程结束后自动释放 MyThread.Start; // 启动线程 end; end. ``` 在上面的示例中,我们创建了一个TMyThread类,继承自TThread类,并重写了Execute方法。在Execute方法中,我们可以编写我们自己的多线程任务逻辑。在Button1Click事件中,我们创建了一个TMyThread对象,并调用其Start方法来启动线程。 以上是Delphi中多线程编程的简单介绍,希望对你有帮助。如果你需要更详细的信息,请参考引用和引用中提到的书籍和文章。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值