1:要想了解多線程,必須先了解線程,而要想了解線程,必須先了解進程,由於線程是依賴於進程而存在。
2:什麼是進程?
經過任務管理器咱們就看到了進程的存在。
而經過觀察,咱們發現只有運行的程序纔會出現進程。
進程:就是正在運行的程序。
進程是系統進行資源分配和調用的獨立單位。每個進程都有它本身的內存空間和系統資源。
3:多進程有什麼意義呢?
單進程的計算機只能作一件事情,而咱們如今的計算機均可以作多件事情。
舉例:一邊玩遊戲(遊戲進程),一邊聽音樂(音樂進程)。
也就是說如今的計算機都是支持多進程的,能夠在一個時間段內執行多個任務。
而且呢,能夠提升CPU的使用率。
問題:
一邊玩遊戲,一邊聽音樂是同時進行的嗎?
不是。由於單CPU在某一個時間點上只能作一件事情。
而咱們在玩遊戲,或者聽音樂的時候,是CPU在作着程序間的高效切換讓咱們以爲是同時進行的。
4:什麼是線程呢?
在同一個進程內又能夠執行多個任務,而這每個任務我就能夠看出是一個線程。
線程:是程序的執行單元,執行路徑。是程序使用CPU的最基本單位。
單線程:若是程序只有一條執行路徑。
多線程:若是程序有多條執行路徑。
5:多線程有什麼意義呢?
多線程的存在,不是提升程序的執行速度。實際上是爲了提升應用程序的使用率。
程序的執行其實都是在搶CPU的資源,CPU的執行權。
多個進程是在搶這個資源,而其中的某一個進程若是執行路徑比較多,就會有更高的概率搶到CPU的執行權。
咱們是不敢保證哪個線程可以在哪一個時刻搶到,因此線程的執行有隨機性。
============================================================
package cn.itcast_01;
/*
* 進程:
* 正在運行的程序,是系統進行資源分配和調用的獨立單位。
* 每個進程都有它本身的內存空間和系統 資源。
* 線程:
* 是進程中的單個順序控制流,是一條執行路徑
* 一個進程若是隻有一條執行路徑,則稱爲單線程程序。
* 一個進程若是有多條執行路徑,則稱爲多線程程序。
*
* 舉例:
* 掃雷程序,迅雷下載
*
* 你們注意兩個詞彙的區別:並行和併發。
* 前者是邏輯上同時發生,指在某一個時間內同時運行多個程序。
* 後者是物理上同時發生,指在某一個時間點同時運行多個程序。
*
* Java程序的運行原理:
* 由java命令啓動JVM,JVM啓動就至關於啓動了一個進程。
* 接着有該進程建立了一個主線程去調用main方法。
*
* 思考題:
* jvm虛擬機的啓動是單線程的仍是多線程的?
* 多線程的。
* 緣由是垃圾回收線程也要先啓動,不然很容易會出現內存溢出。
* 如今的垃圾回收線程加上前面的主線程,最低啓動了兩個線程,因此,jvm的啓動實際上是多線程的。
*/
public class MyThreadDemo {
public static void main(String[] args) {
System.out.println("hello");
new Object();
new Object();
new Object();
new Object();
//...
System.out.println("world");
}
}
====================================================================
package cn.itcast_02;
/*
* 需求:咱們要實現多線程的程序。
* 如何實現呢?
* 因爲線程是依賴進程而存在的,因此咱們應該先建立一個進程出來。
* 而進程是由系統建立的,因此咱們應該去調用系統功能建立一個進程。
* Java是不能直接調用系統功能的,因此,咱們沒有辦法直接實現多線程程序。
* 可是呢?Java能夠去調用C/C++寫好的程序來實現多線程程序。
* 由C/C++去調用系統功能建立進程,而後由Java去調用這樣的東西,
* 而後提供一些類供咱們使用。咱們就能夠實現多線程程序了。
* 那麼Java提供的類 是什麼呢?
* Thread
* 經過查看API,咱們知道了有2中方式實現多線程程序。
*
* 方式1:繼承Thread類。
* 步驟
* A:自定義類MyThread繼承Thread類。
* B:MyThread類裏面重寫run()?
* 爲何是run()方法呢?
* C:建立對象
* D:啓動線程
*/
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 = ne w MyThread();
my1.start();
my2.start();
}
}
package cn.itcast_02;
/*
* 該類要重寫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);
}
}
}
=====================================================================
package cn.itcast_03;
/*
* 如何獲取線程對象的名稱呢?
* public final String getName():獲取線程的名稱。
* 如何設置線程對象的名稱呢?
* public final void setName(String name):設置線程的名稱
*
* 針對不是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();
}
}
*/
package cn.itcast_03;
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);
}
}
}
=====================================================================
package cn.itcast_04;
/*
* 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);
}
}
}
package cn.itcast_04;
public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
==================================================================
package cn.itcast_04;
/*
* 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();
}
}
package cn.itcast_04;
public class ThreadJoin extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
=====================================================
package cn.itcast_04;
/*
* 咱們的線程沒有設置優先級,確定有默認優先級。
* 那麼,默認優先級是多少呢?
* 如何獲取線程對象的優先級?
* public final int getPriority():返回線程對象的優先級
* 如何設置線程對象的優先級呢?
* public final void setPriority(int newPriority):更改線程的優先級。
*
* 注意:
* 線程默認優先級是5。
* 線程優先級的範圍是:1-10。
* 線程優先級高僅僅表示線程獲取的 CPU時間片的概率高,可是要在次數比較多,或者屢次運行的時候才能看到比較好的效果。
*
* 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);
男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女男男女女======
package cn.itcast_04;
/*
* 線程休眠
* 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();
}
}
package cn.itcast_04;
import java.util.Date;
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();
}
}
}
}
===============================================================
package cn.itcast_04;
/*
* 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();
}
}
}
package cn.itcast_04;
import java.util.Date;
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());
}
}
======================================================
package cn.itcast_04;
/*
* 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();
}
}
package cn.itcast_04;
public class ThreadYield extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
Thread.yield();
}
}
}
===================================================
package cn.itcast_05;
/*
* 方式2:實現Runnable接口
* 步驟:
* A:自定義類MyRunnable實現Runnable接口
* B:重寫run()方法
* C:建立MyRunnable類的對象
* D:建立Thread類的對象,並把C步驟的對象做爲構造參數傳遞
*/
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();
}
}
package cn.itcast_05;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
// 因爲實現接口的方式就不能直 接使用Thread類的方法了,可是能夠間接的使用
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
=====================================================================package cn.itcast_06;
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--) + "張票");
}
}
}
}
package cn.itcast_06;
/*
* 某電影院目前正在上映賀歲大片(紅高粱,少林寺傳奇藏經閣),共有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();
}
}
=====================================================================package cn.itcast_07;
/*
* 實現Runnable接口的方式實現
*/
public class SellTicketDemo {
public static void main(Stri ng[] 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();
}
}
package cn.itcast_07;
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--) + "張票");
}
}
}
}
==========================================================
package cn.itcast_08;
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
}
}
}
}
package cn.itcast_08;
/*
* 實現Runnable接口的方式實現
*
* 經過加入延遲後,就產生了連個問題:
* A:相同的票賣了屢次
* CPU的一次操做必須是原子性的
* B:出現了負數票
* 隨機性和延遲致使的
*/
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();
}
}
==========================================
package cn.itcast_09;
/*
* 如何解決線程安全問題呢?
*
* 要想解決問題,就要知道哪些緣由會致使出問題:(並且這些緣由也是之後咱們判斷一個程序是否會有線程安全問題的標準)
* A:是不是多線程環境
* B:是否有共享數據
* C:是否有多條語句操做共享數據
*
* 咱們來回想一下咱們的程序有沒有上面的問題呢?
* A:是不是多線程環境 是
* B:是否有共享數據 是
* C:是否有多條語句操做共享數據 是
*
* 因而可知咱們的程序出現問題是正常的,由於它知足出問題的條件。
* 接下來纔是咱們要想一想如何解決問題呢?
* A和B的問題咱們改變不了,咱們只能想辦法去把C改變一下。
* 思想:
* 把多條語句操做共享數據的代碼給包成一個總體,讓某個線程在執行的時候,別人不能來執行。
* 問題是咱們不知道怎麼包啊?其實我也不知道,可是Java給咱們提供了:同步機制。
*
* 同步代碼塊:
* synchronized(對象){
* 須要同步的代碼;
* }
*
* A:對象是什麼呢?
* 咱們能夠隨便建立一個對象試試。
* B:須要同步的代碼是哪些呢?
* 把多條語句操做共享數據的代碼的部分給包起來
*
* 注意:
* 同步能夠解決安全問題的根本緣由就在那個對象上。該對象如同鎖的功能。
* 多個線程必須是同一把鎖。
*/
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();
}
}
=======================================
package cn.itcast_09;
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--) + "張票");
}
}
}
}
}
===============================================
package cn.itcast_10;
/*
* 舉例:
* 火車上廁所。
*
* 同步的特色:
* 前提:
* 多個線程
* 解決問題的時候要注意:
* 多個線程使用的是同一個鎖對象* 同步的好處
* 同步的出現解決了多線程的安全問題。
* 同步的弊端
* 當線程至關多時,由於每一個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會下降程序的運行效率。
*/
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();
}
}
package cn.itcast_10;
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就出來可,而後就開門。(開)
}
}
}
=====================================================
package cn.itcast_11;
/*
* 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();
}
}
package cn.itcast_11;
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 {
// synchronized (d) {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "張票 ");
// }
// }
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--) + "張票 ");
}
}
}
class Demo {
}
======================================================