synchronized同步
加synchronization的前提
1.必须两个或者两个以上的线程同时访问一个共享资源
2.必须保证同步中只能有一个线程在运行
synchronization锁的是共享对象,而不是代码
1.同步代码块
语法:synchronization(共享资源,共享对象,需要是Object的子类){
核心业务逻辑
}
代码示例:
package com.juc.ticket;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 20:13
* @Description 同步代码块的用法
*/
public class Ticket implements Runnable {
private static int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "----正在出售第" + (ticket--) + "张票");
}
}
}
}
public static void main(String[] args) {
Ticket ticket = new Ticket();
for (int i = 1; i <= 5; i++) {
new Thread(ticket, "买票口" + i).start();
}
}
}
2.同步方法的用法
直接在要锁定的资源上加上synchronized关键字就好。
package com.juc.ticket;
public class T_002 implements Runnable {
private static int ticket = 5;
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里调用同步方法不加this,调用的方法只属于当前对象,加this就属于类
this.test();
}
}
private synchronized void test() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "---正在出售第" + (ticket--) + "张票");
}
}
public static void main(String[] args) {
T_002 t_002 = new T_002();
for (int i = 1; i <= 5; i++) {
new Thread(t_002, "买票口" + i).start();
}
}
}
以上两种加锁方式有什么区别
同步代码块需要指定加锁对象
同步方法不需要指定加锁对象,方法调用的时候记得加上this,让这个方法归属于类,而不是当前对象。
线程同步小结
同步监视器
1.synchronized(obj){} 的obj成为同步监视器
2.同步代码块中的同步监视器可以是任何对象,推荐共享资源对象作为同步监视器
3.同步方法中无需指定同步监视器,因为同步方法的同步监视器是this,也是该对象本身。
同步监视器执行的过程
1.线程一访问cpu资源并锁定同步监视器,让其他线程等待,自己执行其中代码
2.线程二访问cpu资源,发现监视器被锁,无法访问,只能等待释放。
3.线程一访问完毕并解锁同步监视器
4.线程二访问cpu资源,发现同步监视器未锁,锁定并访问,直至最后访问完毕释放锁,其他的线程才能抢占资源,谁先抢到谁先用。
小练习
使用synchronized实现张三和妻子同时去银行取钱的过程。
示例代码:
package com.juc.bank;
import java.math.BigDecimal;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 21:12
* @Description 张三和张三的妻子同时取走500元
*/
public class Take implements Runnable {
private BigDecimal account = new BigDecimal(1500);
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (account) {
if (account.compareTo(BigDecimal.valueOf(500)) >0) {
System.out.println(Thread.currentThread().getName() + "----取走了500.00元");
System.out.println("剩余:" + (account = account.subtract(BigDecimal.valueOf(500))));
}
}
}
}
public static void main(String[] args) {
Take take = new Take();
new Thread(take, "张三").start();
new Thread(take, "张三的妻子").start();
}
}
死锁问题
1.同步可以保证资源共享操作的正确性,但是过多同步也会产生死锁问题。
2.那么死锁什么原因产生的呢?
死锁一般情况下表示相互等待,是程序运行时出现的一种情况
例如下图
生产者与消费者
1.生产者不断的生产,消费者不断的取走生产者生产的产品
2.生产者将生产的产品放在一个公共的区域中,消费者去这个区域消费生产者生产的产品。
过程图
示例代码1
共享资源
package com.juc.pc.c_001;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 23:05
* @Description 共享资源
*/
public class Goods {
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 23:04
* @Description 产品名称
*/
private String name;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 23:05
* @Description 品牌
*/
private String brand;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
消费者代码
package com.juc.pc.c_001;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 23:40
* @Description 消费者
*/
public class Consumer implements Runnable {
private Goods goods;
public Consumer() {
}
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费了" + goods.getBrand() + "--" + goods.getName());
}
}
}
生产者代码
package com.juc.pc.c_001;
import com.juc.bank.Take;
/**
* @Author zcm
* @Email [email protected]
* @date 2020/5/25 23:06
* @Description 生产者
*/
public class Producer implements Runnable {
private Goods goods;
public Producer() {
}
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
goods.setBrand("哇哈哈");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
goods.setName("矿泉水");
}else{
goods.setBrand("旺仔");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
goods.setName("小馒头");
}
System.out.println("生产者生产了"+goods.getBrand()+"---"+goods.getName());
}
}
}
测试代码
package com.juc.pc.c_001;
public class Test {
public static void main(String[] args) {
Goods goods = new Goods();
Producer producer = new Producer(goods);
Consumer consumer = new Consumer(goods);
new Thread(producer).start();
new Thread(consumer).start();
}
}
执行结果:
解决方案:
加锁synchronized
示例代码2:
共享代码
package com.java0526.pc.c_002;
/**
* @program: untitled
* @ClassName Goods
* @Description
* @Author zcm
* @Date 2020/5/26 12:58
* @Version V1.0
*/
public class Goods {
private String brand;
private String name;
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;
}
/**
* @Description: 生产者开始生产
* @Author: zcm
* @return: Param:
* @Date:2020/5/26 13:03
*/
public synchronized void set(String brand, String name) {
this.setBrand(brand);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setName(name);
System.out.println("生产者开始生产产品:" + this.getBrand() + "---" + this.getName());
}
/**
* @Description: 消费者开始消费产品
* @Author: zcm
* @return: Param:
* @Date:2020/5/26 13:03
*/
public synchronized void get() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者开始消费产品:" + this.getBrand() + "---" + this.getName());
}
}
生产者代码
package com.java0526.pc.c_002;
/**
* @program: untitled
* @ClassName Producer
* @Description
* @Author zcm
* @Date 2020/5/26 13:18
* @Version V1.0
*/
public class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
public Producer() {
}
@Override
public void run() {
for (int i = 1; i <=10; i++) {
if (i % 2 == 0) {
this.goods.set("娃哈哈", "矿泉水");
} else {
this.goods.set("旺仔", "小馒头");
}
}
}
}
消费者代码
package com.java0526.pc.c_002;
/**
* @program: untitled
* @ClassName Consumer
* @Description
* @Author zcm
* @Date 2020/5/26 13:18
* @Version V1.0
*/
public class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
public Consumer() {
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
goods.get();
}
}
}
测试代码
package com.java0526.pc.c_002;
/**
* @program: untitled
* @ClassName Test
* @Description
* @Author zcm
* @Date 2020/5/26 13:25
* @Version V1.0
*/
public class Test {
public static void main(String[] args) {
Goods goods=new Goods();
Producer producer=new Producer(goods);
Consumer consumer=new Consumer(goods);
new Thread(producer).start();
new Thread(consumer).start();
}
}
执行结果:
实例三:
共享对象代码:
package com.java0526.pc.c_003;
/**
* @program: untitled
* @ClassName Goods
* @Description
* @Author zcm
* @Date 2020/5/26 12:58
* @Version V1.0
*/
public class Goods {
private String brand;
private String name;
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;
}
/**
* @Description: 默认是不存在商品的,如果值等于true的话,代表有商品
* @Author: zcm
* @return: Param:
* @Date:2020/5/26 13:40
*/
private Boolean flag = false;
/**
* @Description: 生产者开始生产
* @Author: zcm
* @return: Param:
* @Date:2020/5/26 13:03
*/
public synchronized void set(String brand, String name) {
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;//生产完毕,等待消费者消费
notify();//唤醒消费者去消费商品
}
/**
* @Description: 消费者开始消费产品
* @Author: zcm
* @return: Param:
* @Date:2020/5/26 13:03
*/
public synchronized void get() {
//生产者还没有生产商品,不能去消费
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());
//消费完毕,设为false。提醒生产者该去生产商品了
flag = false;
notify();//唤醒生产者去生产
}
}
生产者代码:
package com.java0526.pc.c_003;
/**
* @program: untitled
* @ClassName Producer
* @Description
* @Author zcm
* @Date 2020/5/26 13:18
* @Version V1.0
*/
public class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
for (int i = 1; i <=10; i++) {
if (i % 2 == 0) {
this.goods.set("娃哈哈", "矿泉水");
} else {
this.goods.set("旺仔", "小馒头");
}
}
}
}
消费者代码:
package com.java0526.pc.c_003;
/**
* @program: untitled
* @ClassName Consumer
* @Description
* @Author zcm
* @Date 2020/5/26 13:18
* @Version V1.0
*/
public class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
public Consumer() {
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
goods.get();
}
}
}
执行结果:
以上只是针对简单的消费者和生产者。