2017 - 10 -29 多線程 兩種方式 同步代碼塊

1 多線程程序的引入
如果程序只有一條執行路徑,那麼該程序就是單線程程序。
如果程序有多條執行路徑,那麼該程序就是多線程。


2進程概述及多進程的意義
(1)要想了解多線程,必須先了解線程,而要想了解線程,必須先了解進程,因爲線程是依賴於進程而存在。
(2)什麼是進程?
      通過任務管理器我們就看到了進程的存在。
      而通過觀察,我們發現只有運行的程序纔會出現進程。
      進程:就是正在運行的程序,是系統進行資源分配和調用的獨立單位,每一個進程都有它自己的內存空間和系統資源。
(3)多進程有什麼意義呢?
      單進程的計算機只能做一件事情,而我們現在的計算機都可以做多件事情。
      比如:一邊玩遊戲(遊戲進程),一邊聽音樂(音樂進程)。
      也就是說現在的計算機都是支持多進程的,就可以在一個時間段內執行多個任務。
      並且呢,可以提高CPU的使用率。
      問題:
        一邊玩遊戲,一邊聽音樂是同時進行的嗎?
        不是,因爲CPU(單核)在某一個時間點上只能做一件事情。
        而我們在玩遊戲,或者聽音樂的時候,是CPU在作者程序間的高效切換讓我們覺得是同時進行的。
(4)什麼是線程呢?
      在同一個進程內又可以執行多個任務,而這每一個任務我就可以看出是一個線程。
      線程:是程序的執行單元,執行路徑,是程序使用CPU的最基本單位。
      單線程:如果程序只有一條執行路徑。
      多線程:如果程序有多條執行路徑。
  舉例:掃雷遊戲,迅雷下載(同時多個下載),360(同時體檢和木馬檢測)
(5)多線程有什麼意義呢?
      多線程的存在,不是提高程序的執行速度,其實是爲了提高應用程序的使用率。
      程序的執行其實都是在搶CPU的資源,CPU的執行權。
      多個進程是在搶這個資源,而其中的某一個進程如果執行路徑比較多,就會有更高的機率搶到CPU的執行權。
      我們是不敢保證哪一個縣城能夠在哪個時刻搶到,所以線程的執行有隨機性。
(6)大家注意兩個詞彙:並行和併發
      前者是邏輯上同時發生,指在某一個時間內同時運行多個程序。
      後者是物理上同時發生,指在某一個時間點同時運行多個程序。
3 java程序運行原理
   由java命令啓動JVM,JVM啓動就相當於啓動了一個進程。
   接着由該進程創建了一個主線程去調用main方法。
思考題:
     jvm虛擬機的啓動時單線程還是多線程的?
     多線程的。
     原因是垃圾回收器也要先啓動,否則很容易會出現內存溢出。
     現在垃圾回收線程加上前面的主線程,最低啓動了兩個線程,所以,jvm的啓動是多線程的。

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

  那麼Java提供的類是什麼呢?
       Thread
       通過查看API,我們知道了有2種方式實現多線程程序。
  方式1:繼承Thread類
     步驟 
         A:自定義類MyThread繼承Thread類
         B:MyThread類裏面重寫run()
                 爲什麼是run()方法呢?
         C:創建對象

         D:啓動線程


5 多線程方式1
   該類要重寫run()方法,爲什麼呢?
   不是類中的所有代碼都需要被線程執行。
   而這個時候,爲了區分哪些代碼能夠被線程執行,java提供了Thread類中的run()用來包含那些被線程執行的代碼。
---------------------------------------------------
   public class MyThread extends Thread {
@Override
public void run() {
// 自己寫代碼
// System.out.println("好好學習,天天向上");
// 一般來說,被線程執行的代碼肯定是比較耗時的。所以我們用循環改進
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
// 創建線程對象
// MyThread my = new MyThread();
// 啓動線程  實現多線程效果 調用兩次
// my.run();  // 輸出0-9
// my.run();  // 輸出0-9
                //調用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();
                // 循環太小的話,如果電腦性能太好(x<10,x<100之類的,後來改成200),會出現正常順序
                //輸出 當my1輸出0-22時,my2開始輸出0-117,然後my1開始輸出23-199
 
        }
}

**6 獲取和設置線程對象名稱
如何獲取線程對象的名稱呢?
public final String getName():獲取線程的名稱。
如何設置線程對象的名稱呢?
public final void setName(String name):設置線程的名稱
-----------------------------------------------------
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);
}
}
}
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();
                //輸出:林青霞:0 劉意:0  劉意:1 劉意:2  林青霞:1 。。。
//帶參構造方法給線程起名字 需要創建構造方法傳向父類
// MyThread my1 = new MyThread("林青霞");
// MyThread my2 = new MyThread("劉意");
// my1.start();
// my2.start();
//我要獲取main方法所在的線程對象的名稱,該怎麼辦呢?
//遇到這種情況,Thread類提供了一個很好玩的方法:
//public static Thread currentThread():返回當前正在執行的線程對象
System.out.println(Thread.currentThread().getName());
}
}

7 線程調度及獲取和設置線程優先級
(1)設置優先級
我們的線程沒有設置優先級,肯定有默認優先級。
那麼,默認優先級是多少呢?
如何獲取線程對象的優先級?
       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);
}
}
}
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();
}
}

(2)線程控制之休眠線程
 線程休眠
    public static void sleep(long millis)
------------------------------------------------------
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 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();
                //基本上1秒中出現一次
}
}

(3)線程控制之加入線程
public final void join():等待該線程終止。
------------------------------------------
public class ThreadJoin extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
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();
}
}
(4)線程控制之禮讓線程
 public static void yield():暫停當前正在執行的線程對象,並執行其他線程
---------------------------------------------
public class ThreadYield extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
Thread.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();
               //讓多個線程的執行更和諧,但是不能靠它保證一人一次。
               //輸出 林青霞:0  劉意:0  林青霞:1 劉意:1  。。。。

}
}

(5)線程控制之守護線程
public final void setDaemon(boolean on):將該線程標記爲守護線程或用戶線程。
當正在運行的線程都是守護線程時,Java 虛擬機退出。 該方法必須在啓動線程前調用。 
----------------------------------------------
public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
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);
                //當劉備輸出結束後,關羽和張飛輸出一些後就結束
}
}
}

**(6)線程控制之中斷線程
public final void stop():讓線程停止,過時了,但是還可以使用。
public void interrupt():中斷線程。 把線程的狀態終止,並拋出一個InterruptedException。
---------------------------------------------------------------
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 class ThreadStopDemo {
public static void main(String[] args) {
ThreadStop ts = new ThreadStop();
ts.start();
// 你超過三秒不醒過來,我就乾死你
try {
Thread.sleep(3000); //3000毫秒,3秒
// ts.stop(); //後面的線程全部停止,所以不建議使用
ts.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
8 線程生命週期圖解
***面試題:線程的生命週期?
新建:創建線程對象
就緒:有執行資格,沒有執行權
運行:有執行資格,有執行權
(可能出現)阻塞:由於一些操作讓線程處於了該狀態,沒有執行資格,沒有執行權
                而另一些操作可以把它激活,激活後處於就緒狀態
死亡:線程對象編程垃圾,等待被回收
 
9 多線程方式2 
方式2:實現Runnable接口
步驟:
A:自定義類MyRunnable實現Runnable接口
B:重寫run()方法
C:創建MyRunnable類的對象
D:創建Thread類的對象,並把C步驟的對象作爲構造參數傳遞
----------------------------------------------------------------
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) { 
// 由於實現接口的方式就不能直接使用Thread類的方法了,但是可以間接的使用
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
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();
}
}
10 多線程兩種方式的圖解比較及區別
   
11 賣票案例
某電影院目前正在上映賀歲大片(紅高粱,少林寺傳奇藏經閣),共有100張票,而它有3個售票窗口售票,請設計一個程序模擬該電影院售票。
(1)繼承Thread類來實現。
-----------------------------------------------------------
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--) + "張票");
}
}
}
}
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();
}
}
--------------------------------------------------------
(2)實現Runnable接口的方式實現(更適合數據與代碼分離的事件)
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--) + "張票");
}
}
}
}
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();
}
}

(3) 實現Runnable接口的方式實現 並加入延遲的問題
通過加入延遲後,就產生了連個問題:
A:相同的票賣了多次(正在出售第1張票  正在出售第1張票  正在出售第1張票。。。)
           CPU的一次操作必須是原子性的  
B:出現了負數票(正在出售第0張票  正在出售第-1張票  正在出售第-2張票)
           隨機性和延遲導致的
----------------------------------------------
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 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
}
}
}
}
12 線程安全問題的產生原因分析
如何解決線程安全問題呢?
要想解決問題,就要知道哪些原因會導致出問題:(而且這些原因也是以後我們判斷一個程序是否會有線程安全問題的標準)
  A:是否是多線程環境
  B:是否有共享數據
  C:是否有多條語句操作共享數據

我們來回想一下我們的程序有沒有上面的問題呢?
  A:是否是多線程環境        是
  B:是否有共享數據        是
  C:是否有多條語句操作共享數據
 
由此可見我們的程序出現問題是正常的,因爲它滿足出問題的條件。
接下來纔是我們要想想如何解決問題呢?
A和B的問題我們改變不了,我們只能想辦法去把C改變一下。

思想:
   把多條語句操作共享數據的代碼給包成一個整體,讓某個線程在執行的時候,別人不能來執行。
   問題是我們不知道怎麼包啊?其實我也不知道,但是Java給我們提供了:同步機制。

同步代碼塊:
synchronized(對象){
       需要同步的代碼;
   }

A:對象是什麼呢?
       我們可以隨便創建一個對象試試。
B:需要同步的代碼是哪些呢?
       把多條語句操作共享數據的代碼的部分給包起來
注意:
       同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。
       多個線程必須是同一把鎖。(不是new Object() 而是Object())
--------------------------------------------------------------
public class SellTicket implements Runnable {
// 定義100張票
private int tickets = 100;
//創建鎖對象
private Object obj = new Object();
// @Override
// public void run() {
// while (true) {
// synchronized(new Object()){
// 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 (obj) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "張票");
}
}
}
}
}
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();
}
}


13 同步代碼塊解決線程安全問題的解釋
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就出來可,然後就開門。(開)
}
}
}

14 同步的特點及好處和弊端
同步的特點:
    前提:
         多個線程
    解決問題的時候要注意:
  多個線程使用的是同一個鎖對象
    同步的好處 
同步的出現解決了多線程的安全問題。
    同步的弊端
         當線程相當多時,因爲每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。
 */
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();
}
}

15 同步代碼塊的鎖及同步方法應用和鎖的問題
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) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "張票 ");
}
}
}else {
sellTicket();
}
x++;
}
}

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

//如果一個方法一進去就看到了代碼被同步了,那麼我就再想能不能把這個同步加在方法上呢?
// 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--) + "張票 ");
}
}
}
-----------------------------------------------
A:同步代碼塊的鎖對象是誰呢?
                 任意對象。
B:同步方法的格式及鎖對象問題?
                 把同步關鍵字加在方法上。
                 同步方法是誰呢?
                                       this
C:靜態方法及鎖對象問題?
                 靜態方法的鎖對象是誰呢?
                 類的字節碼文件對象。(反射會講)
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();
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章