1. 程式人生 > 其它 >Java執行緒同步詳解

Java執行緒同步詳解

一、多執行緒執行問題

1、各個執行緒是通過競爭CPU時間而獲得執行機會的
2、各執行緒什麼時候得到CPU時間,佔用多久,是不可預測的
3、一個正在執行著的執行緒在什麼地方被暫停是不確定的

二、執行緒同步

為了解決上述問題,確保共享物件在同一時間只允許被一個執行緒訪問,即執行緒同步,可以使用synchronized和lock來實現。

三、synchronized的使用方式

1、修飾一個程式碼塊,被修飾的程式碼塊稱為同步程式碼塊,作用範圍是大括號{}括起來的程式碼;
2、修飾一個方法,被修飾的方法稱為同步方法,其作用範圍是整個方法;
3、修飾一個靜態方法,作用範圍是整個靜態方法;

例程(銀行存取款)

Class Bank

package com.imooc.bank;

public class Bank {
    private String account;//賬號
    private int balance;//賬戶餘額

    public Bank(String account,int balane){
        this.account=account;
        this.balance=balane;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Bank[賬號:"+account+",餘額:"+balance+"]";
    }
    //存款
    public synchronized void saveAccount(){
        //獲取當前的賬戶餘額
        int balance=getBalance();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //修改金額,存100元
        balance+=100;
        //修改賬餘額
        setBalance(balance);
        //輸出存款後的賬戶餘額
        System.out.println("存款後的賬戶餘額為:"+balance);
    }
    public void drawAccount(){
        synchronized (this){
            //獲得當前賬戶餘額
            int balance=getBalance();
            balance=balance-200;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setBalance(balance);
            System.out.println("取款後的賬戶餘額:"+balance);
        }
    }
}

Class DrawAccount 取款

package com.imooc.bank;

public class DrawAccount implements Runnable{
    Bank bank;
    public DrawAccount(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.drawAccount();
    }
}

Class SaveAccount 存款

package com.imooc.bank;

public class SaveAccount implements Runnable{

    Bank bank;
    public SaveAccount(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.saveAccount();
    }
}

Class Test 測試類

package com.imooc.bank;

public class Test {
    public static void main(String[] args) {
        //建立賬戶,給定餘額為10000
        Bank bank=new Bank("1001",1000);
        //建立執行緒物件
        SaveAccount sa=new SaveAccount(bank);
        DrawAccount da=new DrawAccount(bank);
        Thread save=new Thread(sa);
        Thread draw=new Thread(da);
        save.start();
        draw.start();
        try {
            draw.join();
            save.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bank);
    }
}

經過測試,當不加入synchronized實現執行緒同步時,存款取款的賬戶餘額會發生錯誤,
這裡使用執行緒延時模擬了執行緒被打斷的情況,當存款沒完全進行完時打斷了此執行緒進行取款
此時取款後的餘額不正確。

當加入了執行緒同步後,此問題得到了解決。

lock的使用方式

1、synchroized同步的時候,其中一條執行緒用完會自動釋放鎖,而Lock需要手動釋放,如果不手動釋放,可能造成死鎖
2、使用synchronized如果其中一個執行緒不釋放鎖,那麼其他需要獲取鎖的執行緒會一直等待下取,直到使用完釋放或者出現異常,
而Lock可以使用響應中斷或者使用規定等待時間的鎖
3、synchronized無法得知是否獲取到鎖,而Lock可以做到
4、用ReadWriteLock可以提高多個執行緒進行讀操作的笑了

lock簡單的使用方法

//建立lock物件
Lock lock=new ReentrantLock();
//手動加鎖
lock.lock();
 try{
    // 處理
 }catch(Exception ex){
     // 捕獲異常
}finally{
     // 釋放鎖
     lock.unlock();   
 }

你以為的極限,也許只是別人的起點