程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> No_16_0325 Java基礎學習第二十四天—多線程學習總結

No_16_0325 Java基礎學習第二十四天—多線程學習總結

編輯:JAVA綜合教程

No_16_0325 Java基礎學習第二十四天—多線程學習總結


文檔版本 開發工具 測試平台 工程名字 日期 作者 備注 V1.0       2016.03.25 lutianfei none

第十章 多線程

多線程概述

什麼是進程?

進程:就是正在運行的程序。 進程是系統進行資源分配和調用的獨立單位。每一個進程都有它自己的內存空間和系統資源。

多進程有什麼意義呢?

可以在一個時間段內執行多個任務。 可以提高CPU的使用率。

什麼是線程呢?

在同一個進程內又可以執行多個任務,而這每一個任務我就可以看出是一個線程線程:是程序的執行單元執行路徑。是程序使用CPU的最基本單位單線程:如果程序只有一條執行路徑。 多線程:如果程序有多條執行路徑。

多線程有什麼意義呢?

多線程的存在,不是提高程序的執行速度。其實是為了提高應用程序的使用率。 程序的執行其實都是在搶CPU的資源,CPU的執行權。 多個進程是在搶這個資源,而其中的某一個進程如果執行路徑比較多,就會有更高的幾率搶到CPU的執行權。 我們是不敢保證哪一個線程能夠在哪個時刻搶到,所以線程的執行有隨機性

什麼是並發呢?

大家注意兩個詞匯的區別:並行並發並行邏輯上同時發生,指在某一個時間內同時運行多個程序。 並發物理上同時發生,指在某一個時間點同時運行多個程序。 我們可以實現真正意義上的並發,例如:多個CPU就可以實現,不過得知道如何調度和控制它們。

Java程序運行原理

java 命令會啟動 java 虛擬機,啟動 JVM,等於啟動了一個應用程序,也就是啟動了一個進程。該進程會自動啟動一個 “主線程” ,然後主線程去調用某個類的 main 方法。所以 main方法運行在主線程中。在此之前的所有程序都是單線程的。

思考:jvm虛擬機的啟動是單線程的還是多線程的?

JVM啟動至少啟動了垃圾回收線程主線程,所以是多線程的

如何實現多線程

由於線程是依賴進程而存在的,所以我們應該先創建一個進程出來。進程是由系統創建的,所以我們應該去調用系統功能創建一個進程。 Java是不能直接調用系統功能的,所以,我們沒有辦法直接實現多線程程序。但是Java可以去調用C/C++寫好的程序來實現多線程程序。 由C/C++去調用系統功能創建進程,然後由Java去調用這樣的東西,然後提供一些類供我們使用。我們就可以實現多線程程序了。

多線程的實現方案1

方式1:繼承Thread類。

步驟:

A:自定義類MyThread繼承Thread類。 B:MyThread類裡面重寫run() C:創建對象

D:啟動線程

面試題:run()和start()的區別? run():僅僅是封裝被線程執行的代碼,直接調用是普通方法

start():首先啟動了線程,然後再由jvm去調用該線程的run()方法。

面試題:為什麼重寫run()方法?`

不是類中的所有代碼都需要被線程執行的。為了區分哪些代碼能夠被線程執行,java提供了Thread類中的run()用來包含那些被線程執行的代碼。
/*
 * 需求:我們要實現多線程的程序。
 */
public class MyThreadDemo {
    public static void main(String[] args) {
        // 創建線程對象
        // MyThread my = new MyThread();
        // // 啟動線程
        // my.run();
        // my.run();
        // 調用run()方法為什麼是單線程的呢?
        // 因為run()方法直接調用其實就相當於普通的方法調用,所以你看到的是單線程的效果
        // 要想看到多線程的效果,就必須說說另一個方法:start()
        // 面試題:run()和start()的區別?
        // run():僅僅是封裝被線程執行的代碼,直接調用是普通方法
        // start():首先啟動了線程,然後再由jvm去調用該線程的run()方法。
        // MyThread my = new MyThread();
        // my.start();
        // // IllegalThreadStateException:非法的線程狀態異常
        // // 為什麼呢?因為這個相當於是my線程被調用了兩次。而不是兩個線程啟動。
        // my.start();

        // 創建兩個線程對象
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

        my1.start();
        my2.start();
    }
}


public class MyThread extends Thread {

    @Override
    public void run() {
        // 自己寫代碼
        // System.out.println("好好學習,天天向上");
        // 一般來說,被線程執行的代碼肯定是比較耗時的。所以我們用循環改進
        for (int x = 0; x < 200; x++) {
            System.out.println(x);
        }
    }

}

 

如何獲取和設置線程名稱

Thread類的基本獲取和設置方法

public final String getName():獲取線程的名稱。 public final void setName(String name):設置線程的名稱 通過構造方法也可以給線程起名字

思考:

如何獲取main方法所在的線程名稱呢? public static Thread currentThread()
這樣就可以獲取任意方法所在的線程名稱
/*
 * 針對不是Thread類的子類中如何獲取線程對象名稱呢?
 * public static Thread currentThread():返回當前正在執行的線程對象
 * Thread.currentThread().getName()
 */
public class MyThreadDemo {
    public static void main(String[] args) {
        // 創建線程對象
        //無參構造+setXxx()
        // MyThread my1 = new MyThread();
        // MyThread my2 = new MyThread();
        // //調用方法設置名稱
        // my1.setName("林青霞");
        // my2.setName("劉意");
        // my1.start();
        // my2.start();

        //帶參構造方法給線程起名字
        // MyThread my1 = new MyThread("林青霞");
        // MyThread my2 = new MyThread("劉意");
        // my1.start();
        // my2.start();

        //我要獲取main方法所在的線程對象的名稱,該怎麼辦呢?
        //遇到這種情況,Thread類提供了一個很好玩的方法:
        //public static Thread currentThread():返回當前正在執行的線程對象
        System.out.println(Thread.currentThread().getName());
    }
}

/*
名稱為什麼是:Thread-? 編號

class Thread {
    private char name[];

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }

     private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        //大部分代碼被省略了
        this.name = name.toCharArray();
    }

    public final void setName(String name) {
        this.name = name.toCharArray();
    }


    private static int threadInitNumber; //0,1,2
    private static synchronized int nextThreadNum() {
        return threadInitNumber++; //return 0,1
    }

    public final String getName() {
        return String.valueOf(name);
    }
}

class MyThread extends Thread {
    public MyThread() {
        super();
    }
}

*/



public class MyThread extends Thread {

    public MyThread() {
    }

    public MyThread(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}

 

線程調度

假如我們的計算機只有一個 CPU,那麼 CPU 在某一個時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,才可以執行指令。那麼Java是如何對線程進行調用的呢? 線程有兩種調度模型:
分時調度模型 所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間片 搶占式調度模型 優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那麼會隨機選擇一個,優先級高的線程獲取的 CPU 時間片相對多一些。 Java使用的是搶占式調度模型
public final int getPriority():返回線程對象的優先級 public final void setPriority(int newPriority):更改線程的優 線程默認優先級是5。 線程優先級的范圍是:1-10。 線程優先級高僅僅表示線程獲取的 CPU時間片的幾率高,但是要在次數比較多,或者多次運行的時候才能看到比較好的效果。
public class ThreadPriority extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}




/*
 * 注意:
 * IllegalArgumentException:非法參數異常。
 * 拋出的異常表明向方法傳遞了一個不合法或不正確的參數。 
 *
 */
public class ThreadPriorityDemo {
    public static void main(String[] args) {
        ThreadPriority tp1 = new ThreadPriority();
        ThreadPriority tp2 = new ThreadPriority();
        ThreadPriority tp3 = new ThreadPriority();

        tp1.setName("東方不敗");
        tp2.setName("岳不群");
        tp3.setName("林平之");

        // 獲取默認優先級
        // System.out.println(tp1.getPriority());
        // System.out.println(tp2.getPriority());
        // System.out.println(tp3.getPriority());

        // 設置線程優先級
        // tp1.setPriority(100000);

        //設置正確的線程優先級
        tp1.setPriority(10);
        tp2.setPriority(1);

        tp1.start();
        tp2.start();
        tp3.start();
    }
}

 

線程控制

我們已經知道了線程的調度,接下來我們就可以使用如下方法對象線程進行控制 線程休眠
public static void sleep(long millis) 線程加入
public final void join() 線程禮讓
public static void yield() 後台線程
public final void setDaemon(boolean on)

中斷線程

public final void stop() public void interrupt()

案例:線程休眠

public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x + ",日期:" + new Date());
            // 睡眠
            // 困了,我稍微休息1秒鐘
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}




/*
 * 線程休眠
 *        public static void sleep(long millis)
 */
public class ThreadSleepDemo {
    public static void main(String[] args) {
        ThreadSleep ts1 = new ThreadSleep();
        ThreadSleep ts2 = new ThreadSleep();
        ThreadSleep ts3 = new ThreadSleep();

        ts1.setName("林青霞");
        ts2.setName("林志玲");
        ts3.setName("林志穎");

        ts1.start();
        ts2.start();
        ts3.start();
    }
}

 

案例:線程加入
public class ThreadJoin extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}



/*
 * public final void join():等待該線程終止。 
 */
public class ThreadJoinDemo {
    public static void main(String[] args) {
        ThreadJoin tj1 = new ThreadJoin();
        ThreadJoin tj2 = new ThreadJoin();
        ThreadJoin tj3 = new ThreadJoin();

        tj1.setName("李淵");
        tj2.setName("李世民");
        tj3.setName("李元霸");

        tj1.start();
        try {
            tj1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        tj2.start();
        tj3.start();
    }
}

 

案例:線程禮讓
public class ThreadYield extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
            Thread.yield();
        }
    }
}



/*
 * public static void yield():暫停當前正在執行的線程對象,並執行其他線程。 
 * 讓多個線程的執行更和諧,但是不能靠它保證一人一次。
 */
public class ThreadYieldDemo {
    public static void main(String[] args) {
        ThreadYield ty1 = new ThreadYield();
        ThreadYield ty2 = new ThreadYield();

        ty1.setName("林青霞");
        ty2.setName("劉意");

        ty1.start();
        ty2.start();
    }
}

 

案例:後台線程
public class ThreadDaemon extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}


/*
 * public final void setDaemon(boolean on):將該線程標記為守護線程或用戶線程。
 * 當正在運行的線程都是守護線程時,Java 虛擬機退出。 該方法必須在啟動線程前調用。 
 * 
 * 游戲:坦克大戰。
 */
public class ThreadDaemonDemo {
    public static void main(String[] args) {
        ThreadDaemon td1 = new ThreadDaemon();
        ThreadDaemon td2 = new ThreadDaemon();

        td1.setName("關羽");
        td2.setName("張飛");

        // 設置收獲線程
        td1.setDaemon(true);
        td2.setDaemon(true);

        td1.start();
        td2.start();

        Thread.currentThread().setName("劉備");
        for (int x = 0; x < 5; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}

 

案例:中斷線程
public class ThreadStop extends Thread {
    @Override
    public void run() {
        System.out.println("開始執行:" + new Date());

        // 我要休息10秒鐘,親,不要打擾我哦
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // e.printStackTrace();
            System.out.println("線程被終止了");
        }

        System.out.println("結束執行:" + new Date());
    }
}



/*
 * public final void stop():讓線程停止,過時了,但是還可以使用。
 * public void interrupt():中斷線程。 把線程的狀態終止,並拋出一個InterruptedException。
 */
public class ThreadStopDemo {
    public static void main(String[] args) {
        ThreadStop ts = new ThreadStop();
        ts.start();

        // 你超過三秒不醒過來,我就干死你
        try {
            Thread.sleep(3000);
            // ts.stop();
            ts.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

線程的生命周期圖

\

多線程的實現方案2

實現Runnable接口
步驟:
A:自定義類MyRunnable實現Runnable接口 B:重寫run()方法 C:創建MyRunnable類的對象 D:創建Thread類的對象,並把C步驟的對象作為構造參數傳遞 實現接口方式的好處
可以避免由於Java單繼承帶來的局限性。 適合多個相同程序的代碼去處理同一個資源的情況,把線程同程序的代碼,數據有效分離,較好的體現了面向對象的設計思想。
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            // 由於實現接口的方式就不能直接使用Thread類的方法了,但是可以間接的使用
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }

}

/*
 * 方式2:實現Runnable接口
 */
public class MyRunnableDemo {
    public static void main(String[] args) {
        // 創建MyRunnable類的對象
        MyRunnable my = new MyRunnable();

        // 創建Thread類的對象,並把C步驟的對象作為構造參數傳遞
        // Thread(Runnable target)
        // Thread t1 = new Thread(my);
        // Thread t2 = new Thread(my);
        // t1.setName("林青霞");
        // t2.setName("劉意");

        // Thread(Runnable target, String name)
        Thread t1 = new Thread(my, "林青霞");
        Thread t2 = new Thread(my, "劉意");

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

 

多線程程序練習

需求:
某電影院目前正在上映賀歲大片,共有100張票,而它有3個售票窗口售票,請設計一個程序模擬該電影院售票。 兩種方式實現
繼承Thread類 實現Runnable接口 方式一:
public class SellTicket extends Thread {

    // 定義100張票
    // private int tickets = 100;
    // 為了讓多個線程對象共享這100張票,我們其實應該用靜態修飾
    private static int tickets = 100;

    @Override
    public void run() {
        // 定義100張票
        // 每個線程進來都會走這裡,這樣的話,每個線程對象相當於買的是自己的那100張票,這不合理,所以應該定義到外面
        // int tickets = 100;

        // 是為了模擬一直有票
        while (true) {
            if (tickets > 0) {
                System.out.println(getName() + "正在出售第" + (tickets--) + "張票");
            }
        }
    }
}




/*
 * 某電影院目前正在上映賀歲大片(紅高粱,少林寺傳奇藏經閣),共有100張票,而它有3個售票窗口售票,請設計一個程序模擬該電影院售票。
 * 繼承Thread類來實現。
 */
public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建三個線程對象
        SellTicket st1 = new SellTicket();
        SellTicket st2 = new SellTicket();
        SellTicket st3 = new SellTicket();

        // 給線程對象起名字
        st1.setName("窗口1");
        st2.setName("窗口2");
        st3.setName("窗口3");

        // 啟動線程
        st1.start();
        st2.start();
        st3.start();
    }
}

 

方式二:
public class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "正在出售第"
                        + (tickets--) + "張票");
            }
        }
    }
}



/*
 * 實現Runnable接口的方式實現
 */
public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

 

關於電影院賣票程序的思考

我們前面講解過電影院售票程序,從表面上看不出什麼問題,但是在真實生活中,售票時網絡是不能實時傳輸的,總是存在延遲的情況,所以,在出售一張票以後,需要一點時間的延遲
改實現接口方式的賣票程序
每次賣票延遲100毫秒

改進後的電影院售票出現問題

問題
相同的票出現多次
CPU的一次操作必須是原子性的 還出現了負數的票
隨機性和延遲導致的 注意
線程安全問題在理想狀態下,不容易出現,但一旦出現對軟件的影響是非常大的。
public class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;

//    @Override
//    public void run() {
//        while (true) {
//            // t1,t2,t3三個線程
//            // 這一次的tickets = 100;
//            if (tickets > 0) {
//                // 為了模擬更真實的場景,我們稍作休息
//                try {
//                    Thread.sleep(100); // t1就稍作休息,t2就稍作休息
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//
//                System.out.println(Thread.currentThread().getName() + "正在出售第"
//                        + (tickets--) + "張票");
//                // 理想狀態:
//                // 窗口1正在出售第100張票
//                // 窗口2正在出售第99張票
//                // 但是呢?
//                // CPU的每一次執行必須是一個原子性(最簡單基本的)的操作。
//                // 先記錄以前的值
//                // 接著把ticket--
//                // 然後輸出以前的值(t2來了)
//                // ticket的值就變成了99
//                // 窗口1正在出售第100張票
//                // 窗口2正在出售第100張票
//
//            }
//        }
//    }

    @Override
    public void run() {
        while (true) {
            // t1,t2,t3三個線程
            // 這一次的tickets = 1;
            if (tickets > 0) {
                // 為了模擬更真實的場景,我們稍作休息
                try {
                    Thread.sleep(100); //t1進來了並休息,t2進來了並休息,t3進來了並休息,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "正在出售第"
                        + (tickets--) + "張票");
                //窗口1正在出售第1張票,tickets=0
                //窗口2正在出售第0張票,tickets=-1
                //窗口3正在出售第-1張票,tickets=-2
            }
        }
    }
}

 

解決線程安全問題的基本思想

首先想為什麼出現問題?(也是我們判斷是否有問題的標准)
是否是多線程環境 是否有共享數據 是否有多條語句操作共享數據 如何解決多線程安全問題呢?
基本思想:讓程序沒有安全問題的環境。
把多個語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可。

同步的特點

同步的前提
多個線程 多個線程使用的是同一個鎖對象 同步的好處
同步的出現解決了多線程的安全問題。 同步的弊端
當線程相當多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。

解決線程安全問題實現1

同步代碼塊 格式:
synchronized(對象){需要同步的代碼;} 同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。 同步代碼塊的對象可以是哪些呢?
public class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;
    //創建鎖對象
    private Object obj = new Object();


    @Override
        public void run() {
        while (true) {
            // t1,t2,t3都能走到這裡
            // 假設t1搶到CPU的執行權,t1就要進來
            // 假設t2搶到CPU的執行權,t2就要進來,發現門是關著的,進不去。所以就等著。
            // 門(開,關)
            synchronized (obj) { // 發現這裡的代碼將來是會被鎖上的,所以t1進來後,就鎖了。(關)
                if (tickets > 0) {
                    try {
                        Thread.sleep(100); // t1就睡眠了
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票 ");
                    //窗口1正在出售第100張票
                }
            } //t1就出來可,然後就開門。(開)
        }
    }
}




/*
 * 如何解決線程安全問題呢?
 * 注意:
 * 同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。
 * 多個線程必須是同一把鎖。
 */
public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

 

解決線程安全問題實現2:同步方法

就是把同步關鍵字加到方法上 同步方法的鎖對象是什麼呢?
this 如果是靜態方法,同步方法的鎖對象又是什麼呢?
類的字節碼文件對象。 那麼,我們到底使用誰?
如果鎖對象是this,就可以考慮使用同步方法。 否則能使用同步代碼塊的盡量使用同步代碼塊。
同步解決線程安全問題總結
A:同步代碼塊
synchronized(對象) {
需要被同步的代碼;
} 這裡的鎖對象可以是任意對象。 B:同步方法
把同步關鍵字加在方法上。 這裡的鎖對象是this C:靜態同步方法
把同步關鍵字加在方法上。 鎖對象是類的字節碼文件對象。(反射會講)
public class SellTicket implements Runnable {

    // 定義100張票
    private static int tickets = 100;

    // 定義同一把鎖
    private Object obj = new Object();
    private Demo d = new Demo();

    private int x = 0;

    //同步代碼塊用obj做鎖
//    @Override
//    public void run() {
//        while (true) {
//            synchronized (obj) {
//                if (tickets > 0) {
//                    try {
//                        Thread.sleep(100);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println(Thread.currentThread().getName()
//                            + "正在出售第" + (tickets--) + "張票 ");
//                }
//            }
//        }
//    }

    //同步代碼塊用任意對象做鎖
//    @Override
//    public void run() {
//        while (true) {
//            synchronized (d) {
//                if (tickets > 0) {
//                    try {
//                        Thread.sleep(100);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println(Thread.currentThread().getName()
//                            + "正在出售第" + (tickets--) + "張票 ");
//                }
//            }
//        }
//    }

    @Override
    public void run() {
        while (true) {
            if(x%2==0){
                synchronized (SellTicket.class) {//靜態方法的安全鎖,若是普通同步方法則為:this
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "張票 ");
                    }
                }
            }else {
//                synchronized (d) {
//                    if (tickets > 0) {
//                        try {
//                            Thread.sleep(100);
//                        } catch (InterruptedException e) {
//                            e.printStackTrace();
//                        }
//                        System.out.println(Thread.currentThread().getName()
//                                + "正在出售第" + (tickets--) + "張票 ");
//                    }
//                }

                sellTicket();

            }
            x++;
        }
    }



//同步方法:
    //如果一個方法一進去就看到了代碼被同步了,那麼我就再想能不能把這個同步加在方法上呢?
//     private synchronized void sellTicket() {
//            if (tickets > 0) {
//            try {
//                    Thread.sleep(100);
//            } catch (InterruptedException e) {
//                    e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName()
//                        + "正在出售第" + (tickets--) + "張票 ");
//            }
//    }


//靜態同步方法:
    private static synchronized void sellTicket() {
        if (tickets > 0) {
        try {
                Thread.sleep(100);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()
                    + "正在出售第" + (tickets--) + "張票 ");
        }
}
}

class Demo {
}





public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

 

常見線程安全集合

public class ThreadDemo {
    public static void main(String[] args) {
        // 線程安全的類
        StringBuffer sb = new StringBuffer();
        Vector v = new Vector();
        Hashtable h = new Hashtable();

        // Vector是線程安全的時候才去考慮使用的,但是我還說過即使要安全,我也不用你
        // 那麼到底用誰呢?
        // public static  List synchronizedList(List list)
        List list1 = new ArrayList();// 線程不安全
        List list2 = Collections
                .synchronizedList(new ArrayList()); // 線程安全
    }
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved