146.synchronized同步方法與塊
將之前寫的執行緒不安全的web12306模擬搶票程式碼,引入執行緒安全 1的處理辦法
package zy.thread;
public class unsafeWeb12306 implements Runnable{
private boolean flag = true;
private int ticketNums = 10;
public void run() {
while(flag) {
get();
}
}
public void get() {
if(ticketNums <= 0) {
flag = false;
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--);
}
public static void main(String[] args) {
unsafeWeb12306 w1 = new unsafeWeb12306();
System.out.println(Thread.currentThread().getName()); //main
new Thread(w1, "碼蓄").start();
new Thread(w1, "碼農").start();
new Thread(w1, "碼蝗").start();
}
}
以上程式碼的執行結果是,同一張票會被多人獲得,沒有票的時候也能搶到票,是因為對臨界資源票的訪問沒有加鎖
出現負數情況的原因是,最後一張票的時候,多個執行緒同時跑到搶票的步驟,也就是說控制條件沒有限制住。同一張票被多個人搶到的情況是因為每個執行緒都有自己獨立的工作空間,在第一個執行緒將更改完的資料寫回到主存之前,其它執行緒已經將主存的資料拷貝到自己的工作空間,因此就會出現同一張票被多個執行緒搶到的情況,總結就是臨界資源的訪問和執行緒之間同步的問題。
使用synchronized方法,使搶票程式碼執行緒安全 2
package zy.thread;
public class safeWeb12306 implements Runnable{
private boolean flag = true;
private int ticketNums = 10;
public void run() {
while(flag) {
get();
}
}
public synchronized void get() {
if(ticketNums <= 0) {
flag = false;
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--);
}
public static void main(String[] args) {
safeWeb12306 w1 = new safeWeb12306();
System.out.println(Thread.currentThread().getName()); //main
new Thread(w1, "碼蓄").start();
new Thread(w1, "碼農").start();
new Thread(w1, "碼蝗").start();
}
}
synchronized修飾的是safeWeb12306類的成員方法,執行緒執行的時候鎖的就是this物件,this的資料成員的修改都是互斥的
執行緒不安的銀行存取款程式碼
package zy.thread;
public class unsafeAccount {
public static void main(String[] args) {
Account a = new Account(100, "原麥山丘");
drawing boy = new drawing(a, 60, "boyDrawing");
drawing girl = new drawing(a, 80, "girlDrawing");
boy.start();
girl.start();
}
}
//賬戶
class Account{
int money;
String name;
public Account(int money, String name) {
super();
this.money = money;
this.name = name;
}
}
//取款的業務類
class drawing extends Thread{
Account account;
int drawingMoney;
int packetTotal;
public drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run() {
get();
}
public void get() {
if(account.money < drawingMoney)
return;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
packetTotal += drawingMoney;
System.out.println(this.getName() + "賬戶餘額" + account.money);
System.out.println(this.getName() + "口袋餘額" + packetTotal);
}
}
即使加了如下的判斷,在網路延時的情況下,判斷無法發揮作用,最終執行緒不安全,最終賬戶剩餘餘額為負數
if(account.money < drawingMoney)
return;
對以上銀行取款改進
package zy.thread;
public class safeAccount {
public static void main(String[] args) {
synAccount a = new synAccount(100, "原麥山丘");
syndrawing boy = new syndrawing(a, 60, "boyDrawing");
syndrawing girl = new syndrawing(a, 80, "girlDrawing");
boy.start();
girl.start();
}
}
//賬戶
class synAccount{
int money;
String name;
public synAccount(int money, String name) {
super();
this.money = money;
this.name = name;
}
}
//取款的業務類
class syndrawing extends Thread{
synAccount account;
int drawingMoney;
int packetTotal;
public syndrawing(synAccount account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run() {
get();
}
/*
* 給get方法用synchronized,實際是給this加鎖
* 是不能達到執行緒安全的,鎖定的應該是操作的account
* 使用同步塊就可完成
*/
public void get() {
/*同步塊:
* synchronized(account) {...}
* account稱之為同步監視器
* 同步監視器只能被一個執行緒鎖定
* 只有鎖定同步監視器的執行緒才可以執行,不然就處於同步阻塞狀態
*/
if(account.money < drawingMoney)
return;
synchronized(account) {
if(account.money < drawingMoney)
return;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
packetTotal += drawingMoney;
}
System.out.println(this.getName() + "賬戶餘額" + account.money);
System.out.println(this.getName() + "口袋餘額" + packetTotal);
}
}
同步塊語法:
synchronized(物件obj) {...}
obj稱之為同步監視器
-
同步監視器只能被一個執行緒鎖定
-
只有鎖定同步監視器的執行緒才可以執行,不然就處於同步阻塞狀態
在程式同步塊之前要再加上條件判斷,這樣可以提高效率,不用阻塞等待鎖定同步監視器才判斷
if(account.money < drawingMoney)
return;
synchronized(account) {...}
總結:synchronized的成員方法,鎖定的實際時this物件 3
同步塊鎖的實際是成員方法所能訪問的某一個資料成員
操作容器
多執行緒在操作容器時,如果對容器不加鎖就會導致覆蓋操作,執行緒不安全
package zy.thread;
import java.util.List;
import java.util.ArrayList;
public class unsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0; i<10000; ++i) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println("list元素個數:" + list.size());
}
}
對操作的容器進行同步監視,達到執行緒安全
package zy.thread;
import java.util.List;
import java.util.ArrayList;
public class safeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0; i<10000; ++i) {
new Thread(()->{
synchronized(list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
System.out.println("list元素個數:" + list.size());
}
}
相關文章
- 同步方法及同步塊
- 【Java】【多執行緒】同步方法和同步程式碼塊、死鎖Java執行緒
- notion的文字嵌入與同步塊設計概念
- mysql 備份與遷移 資料同步方法MySql
- Concurrency(六: 同步程式碼塊)
- Java同步方法Java
- java同步與非同步Java非同步
- 掌握C#中非同步魔法:同步方法如何優雅呼叫非同步方法C#非同步
- java 執行緒安全問題,解決執行緒安全問題——同步程式碼塊,同步方法,Lock鎖,Object類中wait方法,notify方法。等待喚醒案例。Java執行緒ObjectAI
- setState非同步、同步與進階非同步
- 同步、非同步、阻塞與非阻塞非同步
- 簡單理解同步與非同步非同步
- 同步非同步 與 阻塞非阻塞非同步
- 執行緒與同步非同步執行緒非同步
- OGG同步複製時與相容觸發器解決方法觸發器
- 聊聊執行緒與程式 & 阻塞與非阻塞 & 同步與非同步執行緒非同步
- 非同步處理方法非同步
- Java 非同步呼叫方法Java非同步
- 執行緒同步方法執行緒
- socket阻塞與非阻塞,同步與非同步、I/O模型非同步模型
- 關於非同步方法中的巨集任務與微任務非同步
- AUTOCAD——建立塊與插入塊命令
- asp.net 應用程式中同步方法呼叫非同步方法無響應解決方法ASP.NET非同步
- JS執行機制--同步與非同步JS非同步
- 程式與執行緒、同步與非同步、阻塞與非阻塞、併發與並行執行緒非同步並行
- 前端面試:js同步與非同步問題前端面試JS非同步
- 徹底搞懂同步非同步與阻塞非阻塞非同步
- QT執行緒同步與非同步處理QT執行緒非同步
- javascript非同步與promiseJavaScript非同步Promise
- 非同步與協程非同步
- 5G/NR SSB(同步訊號和PBCH塊)
- 使goroutine同步的方法總結Go
- Linux系統時間同步方法。Linux
- Dubbo原始碼分析(十)同步呼叫與非同步呼叫原始碼非同步
- win10 怎麼取消同步 win10取消同步方法Win10
- 多工同步與互斥概念
- NAS與NAS資料同步
- 頁面中多個script塊之間方法與變數共享問題變數