首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 線程同步:synchronized 同步方法深度解析

     編輯:Java 相關知識

線程同步核心問題

多線程並發訪問共享可變資源時,若不加控制會導致數據競爭、髒讀、更新丟失等問題。同步的兩大原則:

  1. 將共享資源標記為 private 封裝訪問
  2. 對修改共享狀態的代碼使用 synchronized 同步

synchronized 關鍵字規則

  • 只能修飾非抽象方法,不能修飾變量或構造器
  • 實例方法鎖住 this 對象
  • 靜態方法鎖住 Class 對象
  • 同步塊可指定任意對象作為鎖

代碼示例:銀行賬戶並發操作

/** 
 * Java線程:線程的同步
 * @author leizhimin
 */
public class Test {
    public static void main(String[] args) {
        User u = new User("張三", 100);
        MyThread t1 = new MyThread("線程A", u, 20);
        MyThread t2 = new MyThread("線程B", u, -60);
        MyThread t3 = new MyThread("線程C", u, -80);
        MyThread t4 = new MyThread("線程D", u, -30);
        MyThread t5 = new MyThread("線程E", u, 32);
        MyThread t6 = new MyThread("線程F", u, 21);
        t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start();
    }
}

class MyThread extends Thread {
    private User u; private int y;
    MyThread(String name, User u, int y) { super(name); this.u = u; this.y = y; }
    public void run() { u.oper(y); }
}

class User {
    private String code; private int cash;
    User(String code, int cash) { this.code = code; this.cash = cash; }
    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }
    
    /** 業務方法:同步修改賬戶餘額 */
    public synchronized void oper(int x) {
        try {
            Thread.sleep(10L);
            this.cash += x;
            System.out.println(Thread.currentThread().getName() + "運行結束,增加\"" + x + "\",當前用戶賬戶余額為:" + cash);
            Thread.sleep(10L);
        } catch (InterruptedException e) { e.printStackTrace(); }
    }
    @Override public String toString() { return "User{code='" + code + "', cash=" + cash + '}'; }
}

同步版運行結果(正確)

線程A運行結束,增加"20",當前用戶賬戶余額為:120
線程F運行結束,增加"21",當前用戶賬戶余額為:141
線程E運行結束,增加"32",當前用戶賬戶余額為:173
線程C運行結束,增加"-80",當前用戶賬戶余額為:93
線程B運行結束,增加"-60",當前用戶賬戶余額為:33
線程D運行結束,增加"-30",當前用戶賬戶余額為:3
Process finished with exit code 0

最終餘額 3 = 100 + 20 -60 -80 -30 +32 +21,計算正確。

非同步版運行結果(錯誤)

線程A運行結束,增加"20",當前用戶賬戶余額為:61
線程D運行結束,增加"-30",當前用戶賬戶余額為:63
線程B運行結束,增加"-60",當前用戶賬戶余額為:3
線程F運行結束,增加"21",當前用戶賬戶余額為:61
線程E運行結束,增加"32",當前用戶賬戶余額為:93
線程C運行結束,增加"-80",當前用戶賬戶余額為:61
Process finished with exit code 0

最終餘額不一致(出現 61, 63, 3, 93 等),明顯數據競爭導致更新丟失。

Object 監視器方法輔助協作

同步方法/塊中可調用 wait()、notify()、notifyAll() 實現線程間精細協作:

  • wait():釋放鎖並進入等待集,直到被喚醒或超時
  • notify():喚醒單個等待線程
  • notifyAll():喚醒所有等待線程

這是生產者-消費者等經典模式的基礎。

出處:https://lavasoft.blog.51cto.com/62575/221914

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved