1. 程式人生 > 實用技巧 >執行緒的同步

執行緒的同步

執行緒的同步

執行緒的安全問題

  • 多個執行緒執行的不確定性引起執行的結果的不穩定性
  • 多個執行緒對資料的共享,會造成操作的不完整性、會破壞資料(例如視窗買票問題,多個視窗對票數進行共享,會出現兩個視窗賣號碼相同的票給不同的人)

通過同步機制解決執行緒安全問題

方法一:同步程式碼塊

格式

synchronized(同步監視器){

    需要被同步的程式碼

    }

舉例說明

class Thread implements Runnable{

    private Object obj = new Object();

    public void run() {
        //使用類物件充當鎖
        synchronized(obj){
        .......
        }
    }
}

說明

  • 操作共享資料的程式碼即為需要被同步的程式碼
    • 不能多包含程式碼,也不能少包含程式碼
  • 共享資料:多個執行緒共同操作的變數
  • 同步監視器:俗稱鎖
    • 任何一個類的物件都可以來充當鎖
    • 要求多個執行緒必須共用同一把鎖
    • 在實現Runnable介面建立多執行緒的方式中,考慮使用this充當同步監視器
    • 在繼承Thread類建立多執行緒的方式中,慎用this來充當同步監視器,考慮使用當前類來充當同步監視器

特點

  • 好處:解決執行緒的安全問題
  • 侷限性:操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於一個單執行緒的過程,效率低

程式碼實現

實現Runnable介面建立多執行緒的方式
/**
 * 建立三個視窗買票,票數100張:使用實現Runnable介面的方式實現的
 */
class WindowThread implements Runnable{

    private int ticket = 100;
    // private Object obj = new Object();

    public void run() {
        while (true) {
            //此時this:唯一的WindowThread物件
            synchronized(this){// 方式二:synchronized(obj){
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}

public class Test1 {

    public static void main(String[] args) {
        WindowThread window = new WindowThread();

        Thread w1 = new Thread(window);
        Thread w2 = new Thread(window);
        Thread w3 = new Thread(window);

        w1.setName("視窗1");
        w1.start();
        w2.setName("視窗2");
        w2.start();
        w3.setName("視窗3");
        w3.start();
    }
}
繼承Thread類建立多執行緒的方式
class Window extends Thread {
    // 大家公用資料,只有100張票
    private static int ticket = 100;
    private static Object obj = new Object();
    public void run() {
        while (true) {
            //方式二
            synchronized(Window.class){
                // 方式一:synchronized(obj){
                //synchronized(this)錯誤的,此時this代表著三個物件
                if(ticket > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ":" + "買票,票號為" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }

        }
    }
}

public class Test2 {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();

        w1.setName("視窗1");
        w2.setName("視窗2");
        w3.setName("視窗3");

        w1.start();
        w2.start();
        w3.start();
    }
}

同步方法

如果操作共享資料的程式碼完整的宣告在一個方法中,就可以將此方法宣告同步的

格式

利用synchronized 修飾方法

public synchronized void XXX(){

}

public static synchronized void XXX(){

}

說明

  • synchronized修飾方法時鎖定的是呼叫該方法的物件
  • 同步方法仍然涉及到同步監視器,只是不需要我們顯示的宣告
  • 非靜態的同步方法,同步監視器是this
  • 靜態的同步方法,同步監視器是當前類本身(Window.class)

程式碼實現

實現Runnable介面建立多執行緒的方式

非靜態同步方法,呼叫this

class WindowThread3 implements Runnable{

    private int ticket = 100;
    private static boolean isFlag = true;
    // private Object obj = new Object();

    public void run() {
        while (isFlag) {
            show();
        }
    }

    public synchronized void show(){//同步監視器:this
        if (ticket > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket);
            ticket--;
        }else{
            isFlag = false;
        }
    }
}

public class Test3 {
    public static void main(String[] args) {
        WindowThread3 window = new WindowThread3();

        Thread w1 = new Thread(window);
        Thread w2 = new Thread(window);
        Thread w3 = new Thread(window);

        w1.setName("視窗1");
        w1.start();
        w2.setName("視窗2");
        w2.start();
        w3.setName("視窗3");
        w3.start();
    }
}
繼承Thread類建立多執行緒的方式

靜態同步方法,呼叫當前類本身

class Window4 extends Thread{
    private static int ticket = 100;
    private static boolean isFlag = true;

    @Override
    public void run() {
        while(isFlag){
            show();
        }
    }

    public static synchronized void show(){
        //同步監視器:Window.class
        if(ticket > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket);
            ticket--;
        }else{
            isFlag = false;
        }
    }
}

public class Test4 {
    public static void main(String[] args) {
        Window4 w1 = new Window4();
        Window4 w2 = new Window4();
        Window4 w3 = new Window4();

        w1.setName("視窗1");
        w2.setName("視窗2");
        w3.setName("視窗3");

        w1.start();
        w2.start();
        w3.start();
    }
}