2018年6月11日 星期一

五種 synchronized


public class Synchronized {
    public static void main(String[] args) {
        new Synchronized().xxx();
    }
    
    private void xxx() {
        TestSynchronized t = new TestSynchronized();// 1.7 要加 final
    
        new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                t.test5("0123456789");
            }
        }, "t1").start();
    
        new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                t.test5("abcdefghij");
            }
        }, "t2").start();
    }
}
    
    
    
class TestSynchronized {
    public void test1(String data) {
        try {
            TimeUnit.SECONDS.sleep(1);
            synchronized (this) {
                for (int i = 0; i < data.length(); i++) {
                    System.out.print(data.charAt(i));
                }
                System.out.println();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public synchronized void test2(String data) {
        try {
            TimeUnit.SECONDS.sleep(1);
            for (int i = 0; i < data.length(); i++) {
                System.out.print(data.charAt(i));
            }
            System.out.println();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public void test3(String data) {
        try {
            TimeUnit.SECONDS.sleep(1);
            synchronized (TestSynchronized.class) {
                for (int i = 0; i < data.length(); i++) {
                    System.out.print(data.charAt(i));
                }
                System.out.println();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static synchronized void test4(String data) {
        try {
            TimeUnit.SECONDS.sleep(1);
            for (int i = 0; i < data.length(); i++) {
                System.out.print(data.charAt(i));
            }
            System.out.println();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public void test5(String data) {
        try {
            String x = "";
            TimeUnit.SECONDS.sleep(1);
            synchronized (x) {
                for (int i = 0; i < data.length(); i++) {
                    System.out.print(data.charAt(i));
                    x = "ooo" + i; // 修改不影響,因為每個執行緒進來的 x 是一樣的,原子性不用加鎖
                }
                System.out.println();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

※Thread 要使用區域變數在 1.8 可以不加 final,但如果改值還是會編譯錯誤

※synchronized(this) 和寫在普通方法上一樣,只差在鎖的範圍 (wait()只能使用在這兩種);
synchronized(類.class) 和寫在靜態方法上一樣,只差在鎖的範圍
P.S 將 wait 寫在 synchronized 靜態方法裡會編譯錯誤;但如果寫在 synchronized(類.class) 會是執行期錯誤

※兩個線程的鎖必需是相同的鎖,如 xxx 方法的 t.test5() 改成 new TestSynchronized().test5(),因為有 new 了,所以就不是一樣的鎖了
但 test4 是靜態方法,所以 new 幾次都一樣

※test5 的鎖是自己寫的,內容不能改,但因為有字串池的關係,所以改也 ok;如果將「//修改不影響」這行改成 new String 也沒關係(因為每個執行緒進來是一樣的);String x 改成用 new 的方式就不行
.如果改成用 data,那傳進去的字串內容一樣也是 ok 的,因為有字串池;又如果傳進去的值是用 new String("") ,就沒有字串池了,所以就不行了

.Byte、Short、Integer、Long、Character 有 XxxCache 做快取,會在 -128~127,所以這個範圍內的鎖也像字串池一樣;我試了給 127,然後 一直+1 (或者 new Integer()),雖然已超過這個範圍但也是 ok 的(因為每個執行緒進來是一樣的);又如果一開始就超過這個範圍就不行了

※synchronized(this) 如果是鎖整個方法,那就和 synchronized 方法一樣

※synchronized(類別名稱) 如果是鎖整個方法,那就和 synchronized 靜態方法一樣


※練習一

主線程執行 3 次、子線程執行 10 次、主線程再執行 3 次、子線程再執行 10 次,如此循環 5 次

※第一步

public class Outer5Main3Sub10 {
    private void xxx() {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    synchronized (Outer5Main3Sub10.class) {
                        for (int j = 1; j <= 3; j++) {
                            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "子").start();
    
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    synchronized (Outer5Main3Sub10.class) {
                        for (int j = 1; j <= 10; j++) {
                            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "主").start();
    }
    
    public static void main(String[] args) {
        new Outer5Main3Sub10().xxx();
    }
}

※先寫個互斥


※第二步

public class Outer5Main3Sub10 {
    private MainSub ms = new MainSub();
    
    private void xxx() {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    ms.sub(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "子").start();
    
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    ms.main(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "主").start();
    }
    
    public static void main(String[] args) {
        new Outer5Main3Sub10().xxx();
    }
}
    
class MainSub {
    public synchronized void sub(int i) {
        for (int j = 1; j <= 3; j++) {
            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
        }
    }
    
    public synchronized void main(int i) {
        for (int j = 1; j <= 10; j++) {
            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
        }
    }
}

※將同步鎖放到新的類別


※第三步

public class Outer5Main3Sub10 {
    private MainSub ms = new MainSub();
    
    private void xxx() {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    ms.sub(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "子").start();
    
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    ms.main(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "主").start();
    }
    
    public static void main(String[] args) {
        new Outer5Main3Sub10().xxx();
    }
}
    
class MainSub {
    private boolean sub = true;
    
    public synchronized void sub(int i) {
        while(!sub) { // 注意 spurious wake up,所以不要用 if
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 1; j <= 3; j++) {
            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
        }
         sub = false;
         this.notify();
    }
    
    public synchronized void main(int i) {
        while(sub) { // 注意 spurious wake up,所以不要用 if
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 1; j <= 10; j++) {
            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
        }
        sub = true;
        this.notify();
    }
}

※使用喚醒

※API 的 Object 物件的 wait 方法有說明,有時候會有 spurious wake up (偽喚醒),所以使用 while 代替 if



如果第二步不寫類別

public class Outer5Main3Sub10 {
    private boolean sub = true;
    
    private void xxx() {
        Outer5Main3Sub10 sm = new Outer5Main3Sub10();
    
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    synchronized (sm) {
                        while (!sub) {
                            try {
                                sm.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        for (int j = 1; j <= 3; j++) {
                            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
                        }
                        sub = false;
                        sm.notify();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "子").start();
    
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 1; i <= 5; i++) {
                    synchronized (sm) {
                        while (sub) {
                            try {
                                sm.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        for (int j = 1; j <= 10; j++) {
                            System.out.println(Thread.currentThread().getName() + j + ",i=" + i);
                        }
                        sub = true;
                        sm.notify();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "主").start();
    }
    
    public static void main(String[] args) {
        new Outer5Main3Sub10().xxx();
    }
}




※練習二

兩個執行緒,第一個印 1~52;第二個印 A~Z,結果為 12A34B56C...5152Z

private void xxx() {
    SynchronizedTest st = new SynchronizedTest();
    
    new Thread(() -> {
        for (var i = 1; i <= 26; i++) {
            synchronized (st) {
                while (!flag) {
                    try {
                        st.wait();
                    } catch (InterruptedException e) {
                        System.err.println("t1err=" + e.getMessage());
                    }
                }
                System.out.print(i * 2 - 1 + " ");
                System.out.println(i * 2);
                System.out.println("========================");
                flag = false;
                st.notify();
            }
        }
    }).start();
    
    new Thread(() -> {
        for (var i = 1; i <= 26; i++) {
            synchronized (st) {
                while (flag) {
                    try {
                        st.wait();
                    } catch (InterruptedException e) {
                        System.err.println("t2err=" + e.getMessage());
                    }
                }
                System.out.println((char) (i + ('A' - 1)));
                System.out.println("========================");
                flag = true;
                st.notify();
            }
        }
    }).start();
}



沒有留言:

張貼留言