JAVA線程安全相關

如何判斷一個線程是否安全呢?如果那個線程滿足以下三個特點,那麼它的線程就是不安全的:

  1. 是否是多線程環境
  2. 是否有共享數據
  3. 是否有多條語句操作共享數據

那麼該如何避免線程不安全呢?這裏有幾個解決方法:

  • 同步代碼塊(測試不是同一個鎖的情況,測試是同一個鎖的情況)
    synchronized(對象) {
    需要被同步的代碼。
    }
    這裏的對象是任意對象 ,相當於是一把鎖,只要線程進去就把鎖鎖上

代碼演示:

package com.thz_06;
//用同步代碼塊解決代碼線程不安全問題
public class MyThread extends Thread{
    //靜態代碼塊,定義票數
    static int ticket = 100;

    /*
     * A:同步代碼塊(測試不是同一個鎖的情況,測試是同一個鎖的情況)
            synchronized(對象) {
                需要被同步的代碼。
            }
     */
    //創對象
    static Object  ob= new Object();
    static Object  ob1= new Object();
    int x = 0;
    @Override
    public void run() {
        if(x%2==0){
        //同一對象鎖
        synchronized (ob) {
            while(true){
                //休眠100毫秒
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //賣票
                if(ticket>0){
                    System.out.println("這是"+this.getName()+"賣的第"+ticket--+"張票");
                }else{
                    break;
                }
            }
        }
        x++;
        }else{
            addSynchronized();
            x++;
        }
    }
    //同一對象
    private void addSynchronized() {
        synchronized (ob) {
            while(true){
                //休眠100毫秒
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //賣票
                if(ticket>0){
                    System.out.println("這是"+this.getName()+"賣的第"+ticket--+"張票");
                }else{
                    break;
                    }
                    }
                    }
    }

    /*//不同對象,不安全,會賣同樣的票
    private void addSynchronized() {
        synchronized (ob1) {
            while(true){
                //休眠100毫秒
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //賣票
                if(ticket>0){
                    System.out.println("這是"+this.getName()+"賣的第"+ticket--+"張票");
                }else{
                    break;
                    }
                    }
                    }
                    }*/
}
  • 同步方法(僅適用於實現runable接口)
    public synchronized void sellTicket(){同步代碼}
    鎖的關鍵字是this

代碼演示:

package com.thz_07;
//同步方法(僅適用於實現runable接口)
public class MyThread implements Runnable{
    int ticket = 100;
    @Override
    public void run() {
        while(true){
            //休眠
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            if(ticket>0){
                sellTicket();
            }
        }
    }
    private synchronized void sellTicket() {
        synchronized (this) {

            System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
        }

    }

}
  • 靜態同步方法
    類的字節碼對象
    public static synchronized void sellTicket() {
    需要同步的代碼
    }

代碼演示:

package com.thz_08;
/*
 * 靜態同步方法
                類的字節碼對象
 */
public class MyRunnable implements Runnable{
    static int ticket = 100;
    @Override
    public void run() {
        while(true){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if(ticket>0){
            MyRunnable.sellTicket();
        }
        }

    }
    private synchronized static void sellTicket() {
        synchronized (MyRunnable.class) {
            System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
        }

    }

}

但這樣會顯得我們的代碼頁比較多,有時我們也可以用匿名內部類來啓用線程;

new Thread() {
            public void run() {
                ...
            }
        }.start();

        new Thread(new Runnable(){
            public void run() {
                ...
            }
        }).start();

之前我們加的鎖都是不需要手動釋放的,在JDK5之後java引入了一個新的概念,讓我們可以手動上鎖,手動解鎖,這就是LOCK類;
static Lock lock = new ReentrantLock();
加鎖:lock.lock();
釋放鎖:lock.unlock();
可以讓我們明確的知道在哪裏加鎖和釋放鎖。
爲了保證我們創建的鎖一定會被釋放,用一下代碼進行改進
try{….}finally{…..}
代碼演示:

package com.thz_09;
//案例:利用匿名內部類,啓動多個線程
public class SigleThread {
    public static void main(String[] args) {

        //匿名內部類構造Thread對象並調用start方法
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 99; i++) {
                    System.out.println(i);
                }
            }
        }.start();

        new Thread(new Runnable(){
            public void run() {
                for (int i = 100; i < 199; i++) {
                    System.out.println(i);
                }
            };
        }).start();
    }


}

但是引入鎖之後就要避免一個問題的出現,這就是死鎖,什麼是死鎖呢?就是你在一個鎖裏面上了一把鎖,從而導致永遠無法解鎖。

線程裏還有一個比較重要的東西,這就是線程等待和喚醒機制。
鎖對象調用wait()去進行線程等待,等待鎖對象調用notify()去喚醒鎖,在線程等待的時候釋放鎖,從而讓其他程序,只是他自己線程內的程序暫時不運行,直到被喚醒。
代碼演示:

package com.thz_11;

public class NotifyThread extends Thread{
    @Override
    public void run() {
        synchronized (MyLock.obj) {
            //喚醒等待線程
            MyLock.obj.notify();//喚醒正在等待的線程,喚醒的等待線程的鎖對象,必須和等待線程的鎖對象一致
        }
    }

}
package com.thz_11;

public class WaitThread extends Thread{

    @Override
    public void run() {
        synchronized (MyLock.obj) {
            //讓等待線程處於等待狀態
            try {
                MyLock.obj.wait();//當線程處於等待狀態的時候,線程就不會繼續往下執行了
                                  //線程在處於等待的時候,會釋放掉自己手中的鎖
                                 //sleep()這個方法,在線程休息的時候會釋放鎖碼?
                                //答:不會
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("我被喚醒了");
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章