1. 程式人生 > >Java多線程Thread線程安全

Java多線程Thread線程安全

窗口 exception ren 問題 oid sys 其它 同步機制 發出

1.什麽是線程安全問題?

從某個線程開始訪問到訪問結束的整個過程,如果有一個訪問對象被其他線程修改,那麽對於當前線程而言就發生了線程安全問題;

如果在整個訪問過程中,無一對象被其他線程修改,就是線程安全的。

2.線程安全問題產生的根本原因

首先是多線程環境,即同時存在有多個操作者,單線程環境不存在線程安全問題。在單線程環境下,任何操作包括修改操作都是操作者自己發出的,

操作者發出操作時不僅有明確的目的,而且意識到操作的影響。

多個操作者(線程)必須操作同一個對象,只有多個操作者同時操作一個對象,行為的影響才能立即傳遞到其他操作者。

多個操作者(線程)對同一對象的操作必須包含修改操作,共同讀取不存在線程安全問題,因為對象不被修改,未發生變化,不能產生影響。

綜上可知,線程安全問題產生的根本原因是共享數據存在被並發修改的可能,即一個線程讀取時,允許另一個線程修改

看實例:模擬火車站售票窗口,開啟三個窗口售票,總票數為100張

實例一:

package com.yyx.test;

//模擬火車站售票窗口,開啟三個窗口售票,總票數為100張
//存在線程的安全問題
class Window extends Thread {
    static int ticket = 100;

    public void run() {
        while (true) {
            if (ticket > 0) {
                
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--); } else { break; } } } }
public class TestWindow { 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(); } }

實例二:

package com.yyx.test;

//使用實現Runnable接口的方式,售票
/*
 * 此程序存在線程的安全問題:打印車票時,會出現重票、錯票
 */

class Window1 implements Runnable {
    int ticket = 100;

    public void run() {
        while (true) {
            if (ticket > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--);
            } else {
                break;
            }
        }
    }
}

public class TestWindow1 {
    public static void main(String[] args) {
        Window1 w = new Window1();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

3.線程安全的解決

必須讓一個線程操作共享數據完畢以後,其它線程才有機會參與共享數據的操作

1)線程的同步機制 synchronized

同步代碼塊

package com.yyx.test;

/*      同步代碼塊
 *         synchronized(同步監視器){
 *             //需要被同步的代碼塊(即為操作共享數據的代碼)
 *         }
 *         1.共享數據:多個線程共同操作的同一個數據(變量)
 *         2.同步監視器:由一個類的對象來充當。哪個線程獲取此監視器,誰就執行大括號裏被同步的代碼。俗稱:鎖
 *         要求:所有的線程必須共用同一把鎖!
 *         註:在實現的方式中,考慮同步的話,可以使用this來充當鎖。但是在繼承的方式中,慎用this!
 */
class Window implements Runnable {
    int ticket = 50;// 共享數據

    public void run() {
        while (true) {
            // this表示當前對象,本題中即為w
            synchronized (this) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--);
                }
            }
        }
    }
}

public class TestWindow {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

同步方法

package com.yyx.test;
/*    
 * 同步方法
 * 將操作共享數據的方法聲明為synchronized。即此方法為同步方法,能夠保證當其中一個線程執行
 * 此方法時,其它線程在外等待直至此線程執行完此方法 
 */

class Window implements Runnable {
    int ticket = 50;// 共享數據

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

    public synchronized void show() {
        if (ticket > 0) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--);
        }

    }
}

public class TestWindow {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Java多線程Thread線程安全