執行緒同步(重點)
并發
- 同一個物件被多個執行緒同時操作
- 現實生活中,我們會遇到”同一個資源,多個人都想使用“的問題,比如,食堂排隊打飯,每個人都想吃飯,最天然的解決辦法就是,排隊,一個個來,
- 處理多執行緒問題時,多個執行緒訪問同一個物件,并且某些執行緒還想修改這個物件,這時候我們就需要執行緒同步,執行緒同步其實就是一種等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成佇列,等待前面執行緒使用完畢,下一個執行緒再使用,
- 由于同一行程的多個執行緒共享同一塊存盤空間,在帶來方便的同時,也帶來了訪問沖突的問題,為了保證資料在方法中被訪問時的正確性,在訪問時加入鎖機制sychronized,當一個執行緒獲得物件的排它鎖,獨占資源,其他執行緒必須等待,使用后釋放鎖即可.存在以下問題:
- 一個執行緒持有鎖會導致其他所有需要此鎖的執行緒掛起;
- 在多執行緒競爭下,加鎖,釋放鎖會導致比較多的背景關系切換 和 調度延時,引起性能問題;
- 如果一個優先級高的執行緒等待一個優先級低的執行緒釋放鎖 會導致優先級倒置,引起性能問題.
- 但是魚和熊掌不能兼得
三大不安全案例
案例一:
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket b = new BuyTicket();
new Thread(b,"老黃牛").start();
new Thread(b,"網友").start();
new Thread(b,"自己").start();
}
}
class BuyTicket implements Runnable{
// 票
private int ticetNum = 10;
// 外部停止方式
boolean flag = true;
@Override
public void run() {
// 買票
while (flag){
buy();
}
}
private void buy(){
// 判斷是否有票
if(ticetNum<=0){
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 買票
System.out.println(Thread.currentThread().getName()+"拿到"+ticetNum--);
}
}
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Kw0YCqhK-1617838151276)(D:\學習\tupian\多執行緒\執行緒同步\cuo1.png)]](https://img.uj5u.com/2021/04/09/234661091028361.png)
案例二:
// 不安全的取錢
// 兩個人去銀行賬戶,取錢
public class UnsafeBank {
public static void main(String[] args) {
// 賬戶
Account account = new Account("專案基金",100);
Drawing you = new Drawing(account,50,"組長");
Drawing my = new Drawing(account,80,"組員");
you.start();
my.start();
}
}
class Account{
String name;
int money;
public Account(String name, int yue) {
this.name = name;
this.money = yue;
}
}
// 銀行:模擬取錢
class Drawing extends Thread{
Account account;// 賬戶
// 取了多少錢
int drawingMoney;
// 現在手里有多少錢
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
// 取錢
@Override
public void run() {
// 判斷有沒有錢
if (account.money-drawingMoney<0){
System.out.println(this.getName()+"沒錢了,取不了");
return;
}
// sleep可以放大問題的發生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡內余額 = 余額 - 你取的錢
account.money -= drawingMoney;
// 你手里的錢
nowMoney += drawingMoney;
System.out.println(account.name+"余額為:"+account.money);
System.out.println(this.getName()+"手里的錢"+nowMoney);
}
}
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-v9CI9zk0-1617838151283)(D:\學習\tupian\多執行緒\執行緒同步\cuo2.png)]](https://img.uj5u.com/2021/04/09/234661091028362.png)
案例三:
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();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
為什么會出現這樣的情況呢?
因為集合添加的時候,如果中間有重復,它就會添加替換到同一個地方,導致回圈走完陣列資料不足的情況,
-
如果案例三for回圈里的lamdba運算式不清楚
那就來這里看,最清楚的解釋了lambda運算式
-
這里給大家把不安全案例修改為安全案例
Java-----多執行緒【同步方法 與 不安全案例轉換為安全案例】
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/274045.html
標籤:java
下一篇:網路編程(TCP/IP)
