简述一下生产/消费模型
无非就是两种情况
1.操作一个数据,到达设定的条件时,唤醒另外的线程,休眠自己。
2.休眠时,被另外的线程唤醒,继续进行操作。
关键方法:wait() notify()synchronized()
对于单对应生产/消费,可以直接使用wait、和notify,对于多对应生产/消费,只需要在下面添加一句while(设定的条件),具体可以查看JAVA帮助文档或者源码。而synhronized基本和他们配套,具体可看上一篇文章。
接下来,我们使用这种单对应的生产/消费模型来做一个简单的动画,让一个图形从界面由左向右移动,到中间时,变成另一种图形,图形到达边界时,又重新开始。
思路:
1.需要一个主界面来显示我们的图形,以及启动线程。并且创建标志类的对象来以便使用。
2.设置一个标志类,用来作为线程里的判断,同时,也可以从中创建对象,以此方便synchronized的使用。
3.一个线程用来做圆球的运动,当圆球运动到中间,线程唤醒方块线程,休眠自己。
4.一个线程用来做方块运动,一开始设置休眠状态。当被圆球线程唤醒时,方块从中间运动到边界,线程唤醒圆球线程,休眠自己。
第一步,让我们先来创建好窗体,也是我们的主函数。我们需要显示界面,启动两个线程,创建好标志类的对象。这些功能可以写作一个方法window(),最后,在主函数调用这个window()方法即可。
Mod.java
public class Mod extends JFrame{
public void window() {
//窗体创建
this.setTitle("生产消费模型");
this.setSize(500,500);
//居中
this.setLocationRelativeTo(null);
//退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口的背景色
Container co=this.getContentPane();
co.setBackground(Color.white) ;
//可视化
this.setVisible(true);
//传递画布
Graphics g = this.getGraphics();
//标志数据
Warehouse container = new Warehouse();
//方块线程
Consumption L = new Consumption(g,container);
L.setName("方塊綫程");
L.start();
//圆球线程
Production k = new Production(g,container);
k.setName("球形綫程");
k.start();
}
public static void main(String[] agrs) {
Mod o = new Mod();
o.window();
}
}
第二步,标志类的创建。没有太多需要解释的,比较简单。
Warehouse.java
public class Warehouse {
//help为方块线程中的判断标志,作用是一开始让其处于休眠状态
//flag为圆形线程中的判断标志,作用于help类似
int help=1,flag=1;
//temp用储存x上一步的坐标,用来擦除图形运行的轨迹。
int x=0,y=250,temp;
}
第三步,我们的圆球线程。这是要作为一个线程类,我们一定要先继承Thread。这里我们需要一个构造器,来传递主函数Mod当中,我们要用的画布g和标志类Warehouse的对象container。
构造器的语句:
public Production(Graphics g, Warehouse container) {
this.g = g;
this.container = container;
}
线程的方法里,使用标志help判断进行画球、唤醒方块线程,同时也有改变另一个标志flag的操作。具体解释可以看注释里写的内容。
Production.java
public class Production extends Thread {
Graphics g;
Warehouse container;
public Production(Graphics g, Warehouse container) {
this.g = g;
this.container = container;
}
public void run() {
while (true) {
synchronized (container) {
// help为1的时候,小球向做运动
if (container.help == 1) {
g.setColor(Color.BLACK);
g.fillOval(container.x,container.y, 30, 30);
container.temp = container.x;
try {
sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
container.x++;
//擦除上一步轨迹
g.setColor(Color.white);
g.fillOval(container.temp, container.y, 30, 30);
// 当小球运行到屏幕中央,改变flag值唤醒另一个程序,改变help值睡眠自己
if (container.x == 250 || container.x >250) {
if(container.flag == 1) {
container.flag = 2;
container.notify();
container.help = 2;
try {
container.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
}
最后一步,方块线程,和上一步基本是一样的。只是需要一开始就是休眠状态,所以标志类里flag一个作用,就在这里。
public class Consumption extends Thread {
Graphics g;
Warehouse container;
public Consumption(Graphics g, Warehouse container) {
this.container = container;
this.g = g;
}
public void run() {
while (true) {
synchronized (container) {
if(container.flag == 2) {
g.setColor(Color.black);
g.fillRect(container.x, container.y, 30, 30);
container.temp = container.x;
try {
sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
container.x++;
//擦除上一步轨迹
g.setColor(Color.white);
g.fillRect(container.temp, container.y, 30, 30);
// 当方块横坐标大于500时,更改help值唤醒另一个线程,更改flag值使自己睡眠
if (container.x == 500 || container.x > 500) {
if(container.help == 2) {
container.help = 1;
//让x坐标返回起点
container.x = 0;
container.notify();
container.flag = 1;
try {
container.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 当flag = 1时,保持睡眠不动
if (container.flag == 1) {
try {
wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 当flag不等于1时,方块向右移动
}
}
}
}
}
}
整个程序如何运作,大致像下面这张图一样(不是标准的流程图),只是为了更清楚的展示一个过程。
以上,我个人看来,生产/消费模型并不一定要拘泥于存取的模式,他类似于线程之间的互相沟通。”我做事,你可以先不做,当我累了休息一下,就告诉你来做“。这种思路可以运用到很多使用线程的程序中。