问题:
&emsp’两个消费者同时访问一个仓库对象,仓库内只有一个元素的时候,两个消费者并发访问,会有可能产生抢夺资源的问题。
解决:
仓库对象被线程访问的时候,仓库对象被锁定。仓库对象只能被一个线程访问,其他的线程处于等待状态。
利用特征修饰符synchronized,同步,一个时间点只有一个线程访问
线程安全锁两种形式:
1.将synchronized放在方法的结构上 public synchronized void get(){} 锁定的是调用方法时的那个对象
2.将synchronized关键字,放在方法(构造方法,块)的内部
public void get(){
代码
代码
synchronized(对象){
代码
}
代码
}
/*
为什么要这么做呢,因为线程锁定的时候,其他线程必须等到这个线程走完才能走。而当我方法里的代码有非常多,
但会由于并发产生问题的代码只有几行的时候,就必须用这种方法。不然效率就太低了。
好比说去上课这个方法,有走楼梯的代码,走楼梯可以一次性很多人走,但是规定进教室一次只能进一个人。
这样就能发现, 不能因为每次进教室只能进一人而使每次走楼梯也只能走一人。
*/
线程的不同状态来回切换:
执行–等待–执行–等待
wait() 对象.wait();不是当前的这个对象wait,是访问当前这个对象的线程wait();
只等待,不唤醒,会产生一个类似假死的状态,当所有线程进入等待状态,那就没有线程做事情了
notify() notifyAll()唤醒线程
利用线程安全锁,特征修饰符synchronized
两种不同的写法,一定要记住,不管怎么写,锁定的永远是对象
利用方法控制线程状态的来回切换,wait()可以传毫秒值,如果有传,就是等待多久后进入就绪状态,如果没有传,就得等到被唤醒了,才会进入就绪状态
notify() notifyAll()唤醒线程的两个方法
Thread类中的方法:
sleep()静态方法,long类型的毫秒值作为参数
setPriority(1-10);设置线程的优先级
getPriority();获取线程的优先级
优先级越高,越容易获取到CPU分配的时间碎片
sleep和wait
所在类不同,调用方式不同
sleep哪个位置调用,哪个线程等待
wait对象调用这个方法,访问这个对象的其他线程等待
sleep不需要唤醒,wait需要唤醒
sleep不会释放锁,wait等待后会释放锁
生产消费者模型:
主方法:
package com.csdn.study;
public class Test {
public static void main(String[] args){
Producer producer = new Producer(); // 生产者
Consumer consumer1 = new Consumer(); // 消费者
Consumer consumer2 = new Consumer(); // 消费者
producer.start();
consumer1.start();
consumer2.start();
}
}
仓库类:
package com.csdn.study;
import java.util.ArrayList;
public class WareHouse {
// 单例模式,只有一个仓库
private WareHouse(){}
private static WareHouse wareHouse = new WareHouse();
public static WareHouse getInstance(){
return wareHouse;
}
// 仓库里面的集合,存放货物
private ArrayList<String> list = new ArrayList<>();
// 添加元素方法
public synchronized void add(){
if(list.size() < 20){ // 货物不足20个,继续生产
System.out.println("生产者生产第" + (list.size() + 1) + "个货物");
list.add("货物" + (list.size() + 1));
}else{ // 如果已经有20个货物了。停止生产,叫消费者来拿货物
try{
this.notifyAll(); // 先叫醒别人,自己再去睡觉
this.wait(); // 仓库调用wait方法,不是仓库对象等待,
// 是访问仓库的生产者线程等待。
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
// 从仓库里拿的方法
public synchronized void get(){
if(list.size() > 0){ // 还有货物可以拿
System.out.println("消费者拿走" + list.remove(0));// 每次拿走一个
}else{ // 没有货物可以拿了
try {
this.notifyAll(); // 唤醒生产者
this.wait(); // 消费者等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
生产者类:
package com.csdn.study;
public class Producer extends Thread{
public Producer(){}
private WareHouse wareHouse = WareHouse.getInstance();
public void run(){
while(true) { // 不停的生产
wareHouse.add();
try {
Thread.sleep(200); // 每生产一个货物,就睡200毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者类:
package com.csdn.study;
public class Consumer extends Thread{
public Consumer(){}
private WareHouse wareHouse = WareHouse.getInstance();
public void run(){
while(true){ // 不停的拿
wareHouse.get();
try{
Thread.sleep(300); // 每拿走一个货物就睡眠300毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
运行结果:
如果不加特征修饰符synchronized锁定线程,则会发生异常
为什么会发生异常: