一、同步存在的原因
多线程操作给了我们极大的自由来同时操作不同或相同的数据
可问题就在于
不同的线程可能并没有愿意自己的数据被别人更改
此时,同步出现了
即在某一要求下,我的数据不准别人动,直到我取消此需求
二、同步的使用
同步的前提
- 必需至少有两个线程
- 必需是多个线程使用同一资源
- 必需保证同步中只能有一个线程运行
同步synchronized
- 同步方法
- 同步代码块
四、同步监视器
synchronized(obj){}中的obj成为同步监视器
同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为监视器
同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象本身
同步监视器的执行过程
第一个线程访问,锁定同步监视器,执行其中代码
第二个线程访问,发现同步监视器被锁定,无法访问
第一个线程访问完毕,解锁同步监视器
第二个线程访问,发现同步监视器未锁,锁定并访问
三、生产者与消费者问题举例分析
在正常生活情况下,生产者与消费者最基本的关系是供求关系,即一方供给一方求购
在不考虑复杂情况下,最简单的规则是消费者消费生产者生产出来的商品
在测试过程发现主要注意两个问题:
- 商品是公共的,具体的标记商品必需被生产出来才能被消费且只能一次
- 消费者必需确保生产者生产了才可以消费标记商品
1、生产者代码
public class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
goods.set("娃哈哈","矿泉水");
} else {
goods.set("旺仔","小馒头");
}
}
}
}
2、消费者代码
public class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for(int i = 0;i<10;i++){
goods.get();
}
}
}
3、商品代码
public class Goods {
private String brand;
private String name;
//默认是不存在商品的,如果值等于true的话,代表有商品
private boolean flag = false;
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 get(){
/*
* 如果flag等于false的话,意味着生产者没有生产商品,此时消费者无法消费,需要让消费者线程进入到阻塞状态,等待生产者生产,当
* 有商品之后,再开始消费
* */
if (!flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者取走了"+this.getBrand()+"----"+this.getName());
flag = false;
//唤醒生产者去进行生产
notify();
}
//生产者生产商品
public synchronized void set(String brand,String name){
//当生产者抢占到cpu资源之后会判断当前对象是否有值,如果有的话,以为着消费者还没有消费,需要提醒消费者消费,同时
//当前线程进入阻塞状态,等待消费者取走商品之后,再次生产,如果没有的话,不需要等待,不需要进入阻塞状态,直接生产即可
if(flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setBrand(brand);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setName(name);
System.out.println("生产者生产了" + this.getBrand() + "--" + this.getName());
//如果代码执行到此处,意味着已经生产完成,需要将flag设置为true
flag = true;
//唤醒消费者去进行消费
notify();
}
}
4、运行代码
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();
Producer producer = new Producer(goods);
Consumer consumer = new Consumer(goods);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
结果
生产者生产了娃哈哈--矿泉水
消费者取走了娃哈哈----矿泉水
生产者生产了旺仔--小馒头
消费者取走了旺仔----小馒头
生产者生产了娃哈哈--矿泉水
消费者取走了娃哈哈----矿泉水
生产者生产了旺仔--小馒头
消费者取走了旺仔----小馒头
生产者生产了娃哈哈--矿泉水
消费者取走了娃哈哈----矿泉水
生产者生产了旺仔--小馒头
消费者取走了旺仔----小馒头
生产者生产了娃哈哈--矿泉水
消费者取走了娃哈哈----矿泉水
生产者生产了旺仔--小馒头
消费者取走了旺仔----小馒头
生产者生产了娃哈哈--矿泉水
消费者取走了娃哈哈----矿泉水
生产者生产了旺仔--小馒头
消费者取走了旺仔----小馒头
Process finished with exit code 0