应用场景:生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
代码:
1.商品: 属性:品牌,名字
2.线程1:生产者
3.线程2:消费者
//商品类
public class Product {//商品类
//品牌
private String brand;
//名字
private String name;
//setter,getter方法
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ProducerThread extends Thread{//生产者线程
//共享商品
private Product p;
public ProducerThread(Product p){
this.p=p;
}
@Override
public void run() {
for (int i=1;i<=10;i++){//i:生产次数
if (i%2==0){
//生产费列罗巧克力
p.setBrand("费列罗");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setName("巧克力");
}else{
p.setBrand("哈尔滨");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setName("啤酒");
}
System.out.println("生产者生产了:" + p.getBrand() + "-----" + p.getName());
}
}
}
public class CustomerThread extends Thread {//消费者线程
//共享商品
private Product p;
public CustomerThread(Product p){
this.p = p;
}
@Override
public void run() {
for (int i=1;i<=10;i++){//i:消费次数
System.out.println("消费者消费了:" + p.getBrand() + "----" + p.getName());
}
}
}
public static void main(String[] args) {
//共享商品
Product p = new Product();
//创建生产者和消费者线程
ProducerThread pt = new ProducerThread(p);
CustomerThread ct = new CustomerThread(p);
pt.start();
ct.start();
}
运行效果
出现问题:
1.生产者和消费者没有交替输出
2.打印数据错乱,没有加同步
解决问题:
1.同步代码块
public class ProducerThread extends Thread{//生产者线程
//共享商品
private Product p;//这个对象是共享的,所以锁这个
public ProducerThread(Product p){
this.p=p;
}
@Override
public void run() {
for (int i=1;i<=10;i++){//i:生产次数
synchronized(p){
if (i%2==0){
//生产费列罗巧克力
p.setBrand("费列罗");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setName("巧克力");
}else{
p.setBrand("哈尔滨");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setName("啤酒");
}
}
System.out.println("生产者生产了:" + p.getBrand() + "-----" + p.getName());
}
}
}
public class CustomerThread extends Thread {
//共享商品
private Product p;
public CustomerThread(Product p){
this.p = p;
}
@Override
public void run() {
for (int i=1;i<=10;i++){//i:消费次数
synchronized(p){
System.out.println("消费者消费了:" + p.getBrand() + "----" + p.getName());
}
}
}
}
2.利用同步方法解决问题
//在商品类增加同步方法
//生产商品
public synchronized void setProduct(String brand,String name){
//生产费列罗巧克力
this.setBrand(brand);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setName(name);
System.out.println("生产者生产了:" + this.getBrand() + "-----" + this.getName());
}
//消费商品
public synchronized void getProduct(){
System.out.println("消费者消费了:" + this.getBrand() + "----" + this.getName());
}
//在生产者类重写run()修改
@Override
public void run() {
for (int i=1;i<=10;i++){//i:生产次数
if (i%2==0)
p.setProduct("费列罗","巧克力");
else
p.setProduct("哈尔冰","啤酒");
}
}
//消费者类重写run方法中修改
@Override
public void run() {
for (int i=1;i<=10;i++){//i:消费次数
p.getProduct();
}
}
还是留下问题,没有交替输出,使用线程通信完善代码
//商品类
public class Product {//商品类
//品牌
private String brand;
//名字
private String name;
boolean flag=false;//引入一个灯,true 为有产品 false为没产品
//默认情况没有产品 让生产者先生产 然后消费者再消费
//setter,getter方法
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//生产商品
public synchronized void setProduct(String brand,String name){
if(flag == true){//证明有商品,生产者不生产,等着消费者消费
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//下面flag为false,生产
//生产费列罗巧克力
this.setBrand(brand);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setName(name);
System.out.println("生产者生产了:" + this.getBrand() + "-----" + this.getName());
//生产完后
flag=true;
//告诉消费者来消费
notify();
}
//消费商品
public synchronized void getProduct(){
if (!flag) {//flag==false没有商品 ,等待生产者生产
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者消费了:" + this.getBrand() + "----" + this.getName());
//消费完
flag=false;
//通知生产者生产
notify();
}
}
这样生产者与消费者交替输出
在Java对象中,有两种池
锁池------->synchronized
等待池------->wait(),notify(),notifyAll()
如果一个线程调用了某个对象的wait()方法,那么该线程进入到该对象的等待池中(并且已经将锁释放),如果未来的某一时间,另外一个线程调用了相同对象的notify()方法或者notifyAll方法,那么该等待池中的线程就会被唤起,然后进入到对象的锁池里面去获得该对象的锁
如果获得锁成功后,那么该线程就会沿着wait方法之后的路径继续执行,注意是沿着wait方法之后
注意:
wait方法和notify方法 是必须在同步代码块或者同步方法中生效(因为在同步的基础上进行线程的通信才是有效的)
sleep和wait的区别:sleep进入阻塞状态没有释放锁,wait进入阻塞状态但是同时释放了锁
线程生命周期完整图