Java線程:線程的同步-同步方法

Java線程:線程的同步-同步方法
 
線程的同步是保證多線程安全訪問競爭資源的一種手段。
線程的同步是Java多線程編程的難點,每每開發者搞不清楚什麼是競爭資源、何時須要考慮同步,怎麼同步等等問題,固然,這些問題沒有很明確的答案,但有些原則問題須要考慮,是否有競爭資源被同時改動的問題?
 
在本文以前,請參閱《 Java線程:線程的同步與鎖》,本文是在此基礎上所寫的。
 
對於同步,在具體的Java代碼中須要完成一下兩個操做:
把競爭訪問的資源標識爲private;
同步哪些修改變量的代碼,使用synchronized關鍵字同步方法或代碼。
固然這不是惟一控制併發安全的途徑。
 
synchronized關鍵字使用說明
synchronized只能標記非抽象的方法,不能標識成員變量。
 
爲了演示同步方法的使用,構建了一個信用卡帳戶,起初信用額爲100w,而後模擬透支、存款等多個操做。顯然銀行帳戶User對象是個競爭資源,而多個併發操做的是帳戶方法oper(int x),固然應該在此方法上加上同步,並將帳戶的餘額設爲私有變量,禁止直接訪問。
 
 
/** 
* Java線程:線程的同步 
* 
* @author leizhimin 2009-11-4 11:23:32 
*/ 
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 = 0; 

        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; 
        } 

        /** 
         * 業務方法 
         * @param x 添加x萬元 
         */ 
        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
 
 
反面教材,不一樣步的狀況,也就是去掉oper(int x)方法的synchronized修飾符,而後運行程序,結果以下:
線程A運行結束,增長「20」,當前用戶帳戶餘額爲:61 
線程D運行結束,增長「-30」,當前用戶帳戶餘額爲:63 
線程B運行結束,增長「-60」,當前用戶帳戶餘額爲:3 
線程F運行結束,增長「21」,當前用戶帳戶餘額爲:61 
線程E運行結束,增長「32」,當前用戶帳戶餘額爲:93 
線程C運行結束,增長「-80」,當前用戶帳戶餘額爲:61 

Process finished with exit code 0
 
很顯然,上面的結果是錯誤的,致使錯誤的緣由是多個線程併發訪問了競爭資源u,並對u的屬性作了改動。
 
可見同步的重要性。
 
 
注意:
經過前文可知,線程退出同步方法時將釋放掉方法所屬對象的鎖,但還應該注意的是,同步方法中還可使用特定的方法對線程進行調度。這些方法來自於java.lang.Object類。
 
void notify()    
                    喚醒在此對象監視器上等待的單個線程。    
void notifyAll()    
                    喚醒在此對象監視器上等待的全部線程。    
void wait()    
                    致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法。    
void wait( long timeout)    
                    致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量。    
void wait( long timeout,  int nanos)    
                    致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其餘某個線程中斷當前線程,或者已超過某個實際時間量。
 
結合以上方法,處理多線程同步與互斥問題很是重要,著名的生產者-消費者例子就是一個經典的例子,任何語言多線程必學的例子。

本文出自 「熔 巖」 博客,請務必保留此出處http://lavasoft.blog.51cto.com/62575/221914java

相關文章
相關標籤/搜索