小言_互联网的博客

线程等待第六[自学笔记,大神绕路]

239人阅读  评论(0)

问题引入

我们用一段代码模拟手机主板和芯片匹配的过程。
首先我们需要一个放置主板的类和芯片的类;同时需要一个对放置结果展示的类。

//用于模拟放置主板和芯片的类
class Phone implements Runnable {
    private Worker worker = null;
    public Phone (Worker worker) { this.worker = worker; }
    @Override
    public void run() {
        for (int i = 0;i < 10;i++) {
            if (i % 2 == 0) {
                this.worker.setMainBoard("华为主板");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ignored){}
                this.worker.setChip("麒麟");
            } else {
                this.worker.setMainBoard("苹果主板");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ignored){}
                this.worker.setChip("A");
            }
        }
    }
}
//对放置结果展示的类
class Show implements Runnable {
    private Worker worker = null;
    public Show (Worker worker) { this.worker = worker; }
    @Override
    public void run() {
        for (int i = 0;i < 10;i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ignored){}
            System.out.println(this.worker.getMainBoard() + "--" + this.worker.getChip());
        }
    }
}

class Worker {
    private String mainBoard;       //主板的品牌
    private String chip;            //芯片的品牌
    public void setMainBoard(String mainBoard) { this.mainBoard = mainBoard; }
    public String getMainBoard() { return mainBoard; }
    public void setChip(String chip) { this.chip = chip; }
    public String getChip() { return chip; }
}

public class Producer {
    public static void main(String[] args) {
        Worker worker = new Worker();
        new Thread(new Phone(worker)).start();
        new Thread(new Show(worker)).start();
    }
}

截取其中依次执行的结果

很容易就可以看出其中的两个现象:

  • 华为主板被连续多次匹配,而我们的预期是华为一次、苹果一次
  • 苹果主板被匹配到了麒麟芯片

对于上述两种情况,我们分别称之为操作重复和数据错位。

数据错位的解决

这个问题的出现是典型的数据不同步现象,因此解决方案就是线程同步。
对主板和芯片两个数据的操作只有Worker类中的get和set方法,我们首先对Worker类进行修改:

class Worker {
    private String mainBoard;       //主板的品牌
    private String chip;            //芯片的品牌
    //将两个setter缩减成一个并使用synchronized
    public synchronized void set(String mainBoard, String chip) {
        this.mainBoard = mainBoard;
        try {
            Thread.sleep(100);
        } catch (InterruptedException ignored) {}
        this.chip = chip;
    }
    //将两个getter缩减成一个并使用synchronized
    public synchronized String get() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException ignored) {}
        return this.mainBoard + "--" + this.chip;
    }
}

然后下面就是对另外两个类的相应变化了。

class Phone implements Runnable {
    private Worker worker = null;
    public Phone(Worker worker) { this.worker = worker; }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0) {
                this.worker.set("华为主板","麒麟");
            } else {
                this.worker.set("苹果主板","A");
            }
        }
    }
}
class Show implements Runnable {
    private Worker worker = null;
    public Show(Worker worker) { this.worker = worker; }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(this.worker.get());
        }
    }
}
public class Producer {
    public static void main(String[] args) {
        Worker worker = new Worker();
        new Thread(new Phone(worker)).start();
        new Thread(new Show(worker)).start();
    }
}

随机抽取一次运行结果:

显然数据错位的问题被解决了;下面来解决一下重复操作的问题。

操作重复的解决

对于这段代码来说,重复操作是当一个芯片匹配完毕后,被多次展示。

那么从实际生活角度出发,为了解决这个问题,我们应该对展示者的权限做出如下调整:

  • 在未接收到匹配者的结果时,展示者拥有接收来自匹配者的结果的权限,但没有展示的权限
  • 在接收到匹配者的结果之后、向用户展示之前,没有接收来自匹配者的结果的权限,有展示的权限

也就是说,当展示者没有接收到结果时,展示者是可以进行接收,但是不能进行展示;但是当展示者接收到数据之后,但是还未进行展示时,就不能够再次接收数据,必须将本次接收到的数据展示之后才可以进行下一次的数据接收。

对此我们需要用到Object类里的两种方法
线程等待:

public final void wait() throws InterruptedException【让线程在被唤醒之前一直处于等待状态】
public final void wait(long timeout) throws InterruptedException【在规定时间内线程等待】
public final void wait(long timeout, int nanos) throws InterruptedException【在规定时间内线程等待】

线程唤醒:

public final void notify()【唤醒等待的线程】
public final void notifyAll()【唤醒所有等待的线程】

下面来看一下代码(实际上仅有Worker发生改变):

class Worker {
    private String mainBoard;       //主板的品牌
    private String chip;            //芯片的品牌
    private boolean flag = true;    //表示展示者的权限
    /*flag = true;可以接收,不可以展示*/
    /*flag = false;可以展示,不可以接收*/
    public synchronized void set(String mainBoard, String chip) {
        //当flag为false时,让set进行wait,即无权限接收
        if (!this.flag) {
            try {
                super.wait();
            } catch (InterruptedException ignored){}
        }
        this.mainBoard = mainBoard;
        try {
            Thread.sleep(100);
        } catch (InterruptedException ignored) {}
        this.chip = chip;
        //由于拥有接收权限,即一开始的flag为true,因此在接受完之后要修改为false
        this.flag = false;
        //在接受完之后要赋予展示者展示的权限,即唤醒
        super.notify();
    }
    public synchronized String get() {
        //当flag为true时,让get进行wait,即无权限展示
        if (this.flag) {
            try {
                super.wait();
            } catch (InterruptedException ignored){}
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException ignored) {}
        //由于return后面不允许执行任何语句,所以采用finally
        try {
            return this.mainBoard + "--" + this.chip;
        } finally {
            //由于有权限展示,即开始时flag为false,因此在接收完后要修改为true
            this.flag = true;
            //在接收完之后要赋予展示者接受的权限,即唤醒
            super.notify();
        }
    }
}

随机抽取其中一次执行结果:


转载:https://blog.csdn.net/weixin_46192593/article/details/105400786
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场