線程同步關鍵字 synchronized


在java的多線程編程中多個線程操作同一數據時會出現數據錯誤,主要原因是多個線程同時處理數據時同時獲取了數據,但是有些線程沒有來得及操作數據,然后另一個線程獲取到了之前的值,然后引起數據異常

具體代碼如下:

public class SynchronizedMethod {
    public static void main(String[] args) {
        MoneyMethod moneyMethod = new MoneyMethod();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i);
            t1.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i);
            t2.start();
        }
        
    }
}

class MyThread implements Runnable {
    MoneyMethod moneyMethod ;
    /**
     * 
     */
    public MyThread(MoneyMethod moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }
    public void run() {
        moneyMethod.subMoney();
    }
}

class MoneyMethod {
    int money =20;
    public void addMoney() {
        getMoney();
        money++;
    }
    public  void subMoney() {
        getMoney();
        money--;
    }
    public  void getMoney() {
        System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money);
    }
}

結果值如下:

t10:::getMoney:::20
t11:::getMoney:::21
t13:::getMoney:::21
t14:::getMoney:::22
t12:::getMoney:::24
t23:::getMoney:::24
t22:::getMoney:::24
t24:::getMoney:::23
t21:::getMoney:::22
t20:::getMoney:::21

為了避免這種錯誤,在java中使用關鍵字 synchronized 來處理操作

包括兩種操作:在方法前修飾、在代碼塊前修飾

方法前修飾如下:

public class SynchronizedMethod {
    public static void main(String[] args) {
        MoneyMethod moneyMethod = new MoneyMethod();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i);
            t1.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i);
            t2.start();
        }
        
    }
}

class MyThread implements Runnable {
    MoneyMethod moneyMethod ;
    /**
     * 
     */
    public MyThread(MoneyMethod moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }
    public void run() {
        moneyMethod.subMoney();
    }
}

class MoneyMethod {
    int money =20;
    public synchronized void addMoney() {
        getMoney();
        money++;
    }
    public synchronized void subMoney() {
        getMoney();
        money--;
    }
    public synchronized void getMoney() {
        System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money);
    }
}

結果如下:

t10:::getMoney:::20
t13:::getMoney:::21
t12:::getMoney:::22
t11:::getMoney:::23
t14:::getMoney:::24
t20:::getMoney:::25
t21:::getMoney:::24
t22:::getMoney:::23
t23:::getMoney:::22
t24:::getMoney:::21

請注意,構造函數無法同步 - 使用synchronized帶有構造函數關鍵字是語法錯誤,而且各個實例對象的同步方法之間不會相互影響,在靜態方法上添加synchronized會影響所有的實例對象

但是在同步方法中有缺陷,就是如果對象的所有方法都是同步方法,則鎖都是當前對象,所有如果不是操作同一數據,會影響效率。在java中可以使用同步代碼塊來處理這樣的問題

同步方法的代碼如下:

public class SynchronizedCodeBlock {
    public static void main(String[] args) {
        MoneyMethod2 moneyMethod = new MoneyMethod2();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(() -> {
            
                moneyMethod.addMoney();
            }, "t1" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i);
            t2.start();
        }
        
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(() -> {
                
                moneyMethod.addMoney2();
            }, "t3" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i);
            t2.start();
        }

    }
}

class MyThread2 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread2(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
        
        moneyMethod.subMoney();
    }
}

class MyThread3 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread3(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
    
        moneyMethod.subMoney2();
    }
}

class MoneyMethod2 {
    int money = 100;
    int money2 = 600;
    Object lock1 = new Object();
    Object lock2 = new Object();

    public synchronized void addMoney() {
//         (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money++;
//        }

    }

    public synchronized void subMoney() {
//        synchronized (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money--;
//        }
    }

    public synchronized void addMoney2() {
//        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2++;
//        }
    }
    public synchronized void subMoney2() {
//        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2--;
//        }
    }

    public void getMoney() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime());
    }

    public void getMoney2() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime());
    }
}

 

執行結果:

t10:::getMoney:::100:::1540435235076
t44:::getMoney2:::600:::1540435239076
t43:::getMoney2:::599:::1540435243076
t42:::getMoney2:::598:::1540435247076
t41:::getMoney2:::597:::1540435251076
t40:::getMoney2:::596:::1540435255077
t34:::getMoney2:::595:::1540435259077
t33:::getMoney2:::596:::1540435263077
t32:::getMoney2:::597:::1540435267078
t31:::getMoney2:::598:::1540435271078
t30:::getMoney2:::599:::1540435275079
t24:::getMoney:::101:::1540435279080
t23:::getMoney:::100:::1540435283080
t22:::getMoney:::99:::1540435287080
t21:::getMoney:::98:::1540435291080
t20:::getMoney:::97:::1540435295080
t13:::getMoney:::96:::1540435299080
t14:::getMoney:::97:::1540435303080
t11:::getMoney:::98:::1540435307081
t12:::getMoney:::99:::1540435311082

使用了同步代碼塊的代碼:

public class SynchronizedCodeBlock {
    public static void main(String[] args) {
        MoneyMethod2 moneyMethod = new MoneyMethod2();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(() -> {
            
                moneyMethod.addMoney();
            }, "t1" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i);
            t2.start();
        }
        
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表達式實現runable接口
            Thread t1 = new Thread(() -> {
                
                moneyMethod.addMoney2();
            }, "t3" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i);
            t2.start();
        }

    }
}

class MyThread2 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread2(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
        
        moneyMethod.subMoney();
    }
}

class MyThread3 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread3(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
    
        moneyMethod.subMoney2();
    }
}

class MoneyMethod2 {
    int money = 100;
    int money2 = 600;
    Object lock1 = new Object();
    Object lock2 = new Object();

    public  void addMoney() {
        synchronized(lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money++;
        }

    }

    public void subMoney() {
        synchronized (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money--;
        }
    }

    public void addMoney2() {
        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2++;
        }
    }
    public void subMoney2() {
        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2--;
        }
    }

    public void getMoney() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime());
    }

    public void getMoney2() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime());
    }
}

結果:

t10:::getMoney:::100:::1540435584843
t30:::getMoney2:::600:::1540435584844
t23:::getMoney:::101:::1540435588844
t44:::getMoney2:::601:::1540435588845
t24:::getMoney:::100:::1540435592844
t43:::getMoney2:::600:::1540435592845
t22:::getMoney:::99:::1540435596844
t42:::getMoney2:::599:::1540435596846
t20:::getMoney:::98:::1540435600845
t41:::getMoney2:::598:::1540435600847
t21:::getMoney:::97:::1540435604845
t40:::getMoney2:::597:::1540435604847
t14:::getMoney:::96:::1540435608846
t34:::getMoney2:::596:::1540435608848
t13:::getMoney:::97:::1540435612846
t33:::getMoney2:::597:::1540435612848
t11:::getMoney:::98:::1540435616847
t31:::getMoney2:::598:::1540435616849
t12:::getMoney:::99:::1540435620847
t32:::getMoney2:::599:::1540435620849

從以上兩種情況可以發現使用了同步代碼塊后兩個不同鎖的代碼塊可以異步執行

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM