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();
}
}
}
執行結果:
以上只是針對簡單的消費者和生產者。