在此之前我先给出这样一个情景:
锅里有一个饼,有两个饥饿的人同时看到了这个饼,按理来说,只有一个人能够吃到饼,那如果我们把饼看作一个信息,饥饿的人看作两个线程,会发生什么?
package com.Test;
public class Cookies_and_hugry_people extends Thread {
int cookies_num = 1;//表示一块饼
public void run() {
System.out.println(Thread.currentThread().getName()+"来了");
System.out.println("桌上有" + cookies_num + "块饼");
if(cookies_num != 0) {
System.out.println(Thread.currentThread().getName()+"想吃饼");
cookies_num--;
System.out.println(Thread.currentThread().getName() + "吃掉一块饼,还剩" + cookies_num + "块饼");
}
}
public static void main(String[] args) {
Cookies_and_hugry_people obj = new Cookies_and_hugry_people();
new Thread(obj,"小明").start();
new Thread(obj,"小刚").start();
}
}
结果输出为这样
还剩-1块饼?!
显然,两个线程共用了桌上有1块饼的信息最后竟然无中生有暗度陈仓,这显然不是我们希望看到的结果。所以我们可以想办法让一个线程进行完以后再让另一个线程去执行代码
只需要给代码块加上锁就好了
public void run() {
System.out.println(Thread.currentThread().getName() + "来了");
synchronized (this) {//新加代码,给该部分上锁
System.out.println("桌上有" + cookies_num + "块饼");
if (cookies_num != 0) {
System.out.println(Thread.currentThread().getName() + "想吃饼");
cookies_num--;
System.out.println(Thread.currentThread().getName() + "吃掉一块饼,还剩" + cookies_num + "块饼");
}
}
}
其运行结果为
synchronized 字面意思为同步,其后面的括号里为一个对象,即对括号中的对象上锁,在一个线程中,这个对象进入代码块就相当于上了锁,必须等其运行完整个代码后才解锁,让另一个线程对象进来。
生产者消费者模式
先在我们再次假设一个情景,有一个生产饼干的机器,能够源源不断地生产出饼干来,放到桌上,并且能够判断桌上是不是放满了饼,如果桌上放不下了,机器就会停止生产(生产者);然后有饥饿的人,只要桌上有饼就会吃掉饼(消费者)。
package com.Test_producer_and_consumer;
import java.util.ArrayList;
public class Producers extends Thread{
ArrayList<String> cookies=new ArrayList<>();
public Producers(ArrayList<String> cookies) {
this.cookies=cookies;
}
public void run() {
System.out.println("机器准备就绪~~~");
while(true) {
try {
synchronized (cookies) {
Thread.sleep(1000);
if(cookies.size()<3) {
cookies.add("饼"+cookies.size());
System.out.println("机器生产了一个饼,现在有"+cookies.size()+"个饼");
cookies.notify();
}
if(cookies.size()==3) {
System.out.println("桌上装不下饼了,机器停止生产");
cookies.wait();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
将饼装在**ArrayList**队列里面。互斥对象为cookies,当桌上装满了饼则会呼叫饥饿的小明过来吃饼
package com.Test_producer_and_consumer;
import java.util.ArrayList;
public class Resumer extends Thread{
ArrayList<String> cookies =new ArrayList<>();
public Resumer(ArrayList<String> cookies) {
this.cookies=cookies;
}
public void run () {
System.out.println("饥饿的小明准备就绪~~~~");
while(true) {
synchronized (cookies) {
try {
Thread.sleep(1000);
if(cookies.size()>0) {
cookies.remove(0);
System.out.println("小明吃掉了一个饼,现在有"+cookies.size()+"个饼,又可以叫机器做饼了,呼叫机器");
cookies.notify();
}
if(cookies.size()==0) {
System.out.println("桌上的饼吃光了,小明停止吃饼");
cookies.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
然后将两个线程同时启动
package com.Test_producer_and_consumer;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> cookies=new ArrayList<>();
new Producers(cookies).start();
new Resumer(cookies).start();
}
}
运行结果如下
我们可以看到,小明吃了饼以后需要开始呼叫机器,但由于此时程序并没有结束小明不会停止吃饼只有等到饼吃完以后,小明停止吃饼了,机器才能进入自己的代码块,也就是说关于cookies对象的代码块被锁住了
如果我们在生产一个饼之后机器就停下来就会出现这样的情况:
package com.Test_producer_and_consumer;
import java.util.ArrayList;
public class Producers extends Thread{
ArrayList<String> cookies=new ArrayList<>();
public Producers(ArrayList<String> cookies) {
this.cookies=cookies;
}
public void run() {
System.out.println("机器准备就绪~~~");
while(true) {
try {
synchronized (cookies) {
Thread.sleep(1000);
if(cookies.size()<3) {
cookies.add("饼"+cookies.size());
System.out.println("机器生产了一个饼,现在有"+cookies.size()+"个饼");
cookies.notify();
cookies.wait();
}
if(cookies.size()==3) {
System.out.println("桌上装不下饼了,机器停止生产");
// cookies.wait();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
对比之前的Producers类,机器在生产一个饼之后就开始wait了。