多線程並發訪問共享可變資源時,若不加控制會導致數據競爭、髒讀、更新丟失等問題。同步的兩大原則:
private 封裝訪問synchronized 同步this 對象/**
* 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 等),明顯數據競爭導致更新丟失。
同步方法/塊中可調用 wait()、notify()、notifyAll() 實現線程間精細協作:
wait():釋放鎖並進入等待集,直到被喚醒或超時notify():喚醒單個等待線程notifyAll():喚醒所有等待線程這是生產者-消費者等經典模式的基礎。