问题引入
我们用一段代码模拟手机主板和芯片匹配的过程。
首先我们需要一个放置主板的类和芯片的类;同时需要一个对放置结果展示的类。
//用于模拟放置主板和芯片的类
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