多線程新蟆、單例
線程
線程是程序執(zhí)行的一條路徑, 一個(gè)進(jìn)程中可以包含多條線程觅赊。
多線程并發(fā)執(zhí)行可以提高程序的效率, 可以同時(shí)完成多項(xiàng)工作。并行和并發(fā)
并行就是兩個(gè)任務(wù)同時(shí)運(yùn)行琼稻,就是甲任務(wù)進(jìn)行的同時(shí)吮螺,乙任務(wù)也在進(jìn)行。(需要多核CPU)
并發(fā)是指兩個(gè)任務(wù)都請求運(yùn)行,而處理器只能按受一個(gè)任務(wù)鸠补,就把這兩個(gè)任務(wù)安排輪流進(jìn)行萝风,由于時(shí)間間隔較短,使人感覺兩個(gè)任務(wù)都在運(yùn)行紫岩。
并行:兩個(gè)或多個(gè)事件在同一時(shí)刻點(diǎn)發(fā)生规惰。
并發(fā):兩個(gè)或多個(gè)事件在同一時(shí)間段內(nèi)發(fā)生。創(chuàng)建線程
線程類:Thread類和Thread的子類才能稱之為線程泉蝌。
方式一:繼承Thread類歇万。
方式二:實(shí)現(xiàn)Runnable接口。
方式三:匿名內(nèi)部類勋陪。(用的最少)
1贪磺、方式一
public class ThreadDemo {
public static void main (String[] args) {
// 方式一:繼承Thread類
// 4)在main方法中創(chuàng)建線程對象,并啟動線程诅愚。
MyThread myThread = new MyThread();
// 5)開啟線程
myThread.start();
myThread.getName(); //獲取當(dāng)前線程
}
}
// 1)定義一個(gè)類A繼承于java.long. Thread類寒锚。
class MyThread extends Thread{
// 2)在A類中覆蓋Thread的run方法。
@Override
public void run() {
super.run();
// 3)在run方法中編寫需要執(zhí)行的代碼违孝。
for (int i = 0; i < 100; i ++){
Thread.currentThread().setName("線程---2"); // 設(shè)置線程名稱
System.out.println(Thread.currentThread()); // 獲取當(dāng)前線程
}
}
}
好處:可以直接使用Thread類中的方法,代碼簡單刹前。
弊端:如果已經(jīng)有了父類,就不能用這種方法。
2雌桑、方式二
public class ThreadDemo {
public static void main (String[] args) {
// 方式二:實(shí)現(xiàn)Runnable接口
// 4)在main方法中創(chuàng)建線程對象腮郊,并啟動線程。
Thread thread = new Thread(new GameThread());
thread.start();
}
}
// 1)定義一個(gè)類A實(shí)現(xiàn)java.lang.Runnable接口筹燕。
class GameThread implements Runnable{
// 2)在A類中實(shí)現(xiàn)Runnable接口中的run方法轧飞。
@Override
public void run() {
// 3)在run方法中編寫需要執(zhí)行的代碼。
for (int i = 0; i < 100; i ++){
this.setName("線程---1"); // 設(shè)置線程名稱
System.out.println(Thread.currentThread()); // 獲取當(dāng)前線程
}
}
}
好處:即使自己定義的線程類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,而且接口是可以多實(shí)現(xiàn)的撒踪。
弊端:不能直接使用Thread中的方法需要先獲取到線程對象后,才能得到Thread的方法过咬。
3、匿名內(nèi)部類創(chuàng)建線程
public class ThreadDemo {
public static void main (String[] args) {
//方式三:匿名內(nèi)部類
// 1)將Runnable的子類對象傳遞給Thread的構(gòu)造方法
new Thread(new Runnable() {
// 2)重寫run方法
@Override
public void run() {
// 3)將要執(zhí)行的代碼寫在run方法中
for (int i = 0; i < 100; i ++){
Thread.currentThread().setName("線程---3"); // 設(shè)置線程名稱
System.out.println(Thread.currentThread()); //獲取當(dāng)前線程
}
}
}).start(); // 開啟線程
}
}
-
線程休眠
讓執(zhí)行的線程暫停一段時(shí)間制妄,進(jìn)入計(jì)時(shí)等待狀態(tài)掸绞。
方法:state void sleep(long mills)
調(diào)用sleep后,當(dāng)前線程放棄CPU耕捞,sleep線程不會獲得執(zhí)行機(jī)會衔掸,此狀態(tài)下的線程不會釋放同步鎖。
該方法更多的用于網(wǎng)絡(luò)延遲俺抽。
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 20; i >= 0; i--){
System.out.println("倒計(jì)時(shí)" + i + "秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
-
守護(hù)線程
在后臺運(yùn)行的線程敞映,其目的是為其它線程提供服務(wù)。JVM的垃圾回收線程就是典型的后臺線程磷斧。
setDaemon(), 設(shè)置一個(gè)線程為守護(hù)線程, 該線程不會單獨(dú)執(zhí)行, 當(dāng)其他非守護(hù)線程都執(zhí)行結(jié)束后, 自動退出振愿。
特點(diǎn):若所有的前臺線程都死亡捷犹,后臺線程自動死亡,前臺線程沒有結(jié)束冕末,后臺線程是不會結(jié)束的萍歉。
thread.isDaemon() 返回是否為后臺線程。
前臺線程創(chuàng)建的線程默認(rèn)是前臺線程档桃,可以通過setDaemon()方法設(shè)置為后臺線程(setDaemon()方法必須在start方法前調(diào)用)枪孩。后臺線程創(chuàng)建的線程默認(rèn)為后臺線程。
private static void daemonDemo(){
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread());
}
}
}).start();
Thread thread1 = new Thread(){
@Override
public void run() {
super.run();
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread());
}
}
};
Thread thread = new Thread(){
@Override
public void run() {
super.run();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread());
}
}
};
thread.setDaemon(true);
thread.start();
}
-
加入線程
join(), 當(dāng)前線程暫停, 等待指定的線程執(zhí)行結(jié)束后, 當(dāng)前線程再繼續(xù)
join(int), 可以等待指定線程執(zhí)行指定毫秒后繼續(xù)
private static void joinThreadDemo(){
final Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread() + "-----111111");
}
}
};
thread.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (i == 2){
try {
// thread.join(); // 線程1開始執(zhí)行藻肄,執(zhí)行完后線程2才開始執(zhí)行
thread.join(1); // 線程1執(zhí)行1毫米蔑舞,然后線程1和線程2交替執(zhí)行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread() + "-----222222");
}
}
}).start();
}
禮讓線程(了解)
Thread.yield():表示當(dāng)前線程對象提示調(diào)度器自己愿意讓出CPU資源,但是調(diào)度器可以自由忽略該意愿仅炊。
調(diào)用該方法后斗幼,線程進(jìn)入就緒狀態(tài)澎蛛,所以某個(gè)線程調(diào)用了yield方法之后,調(diào)度器又把它調(diào)度出來重新執(zhí)行。
開發(fā)中很少使用該方法体捏,主要用于調(diào)試染苛,它可能有助于因多線程調(diào)度的競爭條件下的錯誤重現(xiàn)。設(shè)置線程優(yōu)先級(了解)
每個(gè)線程都有優(yōu)先級毁兆,優(yōu)先級的高低只和線程獲得執(zhí)行機(jī)會的多少有關(guān)浙滤,與執(zhí)行先后順序無關(guān),執(zhí)行順序取決于CPU的調(diào)度气堕。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
// 設(shè)置優(yōu)先級(建議三個(gè)常量就可以了)
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Thread.currentThread().setPriority(3);
// 獲取當(dāng)前線程優(yōu)先級
Thread.currentThread().getPriority()
每個(gè)線程都有默認(rèn)優(yōu)先級纺腊,主線程默認(rèn)優(yōu)先級是5;
如果A線程創(chuàng)建了B線程茎芭,那么B線程與A線程有相同優(yōu)先級揖膜。
作用不是很明顯
Thread thread1 = new Thread(){
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread() + "-----111");
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread() + "-----222");
}
}
};
thread1.setPriority(10);
thread2.setPriority(1);
thread1.start();
thread2.start();
}
- 線程安全
- 同步代碼塊
當(dāng)多線程并發(fā), 有多段代碼同時(shí)執(zhí)行時(shí), 我們希望某一段代碼執(zhí)行的過程中CPU不要切換到其他線程工作. 這時(shí)就需要同步。
如果兩段代碼是同步的, 那么同一時(shí)間只能執(zhí)行一段, 在一段代碼沒執(zhí)行結(jié)束之前, 不會執(zhí)行另外一段代碼梅桩。
使用 synchronized 關(guān)鍵字加上一個(gè)鎖對象來定義一段代碼, 這就叫同步代碼塊壹粟。
多個(gè)同步代碼塊如果使用相同的鎖對象, 那么他們就是同步的。
// 同步代碼塊
synchronized (this) {
// 多個(gè)線程共享的代碼
}
private static void test(){
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
PrinterTest.print1();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
PrinterTest.print2();
}
}
}).start();
}
class PrinterTest {
ThreadDemo threadDemo = new ThreadDemo();
public static void print1() {
synchronized(threadDemo){ //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對象
System.out.print("你");
System.out.print("是");
System.out.print("豬");
System.out.print("嗎");
System.out.print("\r\n");
}
}
public static void print2() {
synchronized(threadDemo){
System.out.print("無");
System.out.print("糖");
System.out.print("口");
System.out.print("香");
System.out.print("糖");
System.out.print("\r\n");
}
}
}
- 同步方法
使用synchronized修飾的方法宿百。
// 同步方法
synchronized private void doWork () {
// 多個(gè)線程共享的代碼
}
class PrinterTest {
public static void print1() {
synchronized(PrinterTest.class){ //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對象
System.out.print("你");
System.out.print("是");
System.out.print("豬");
System.out.print("嗎");
System.out.print("\r\n");
}
}
/*
* 非靜態(tài)同步函數(shù)的鎖是:this
* 靜態(tài)的同步函數(shù)的鎖是:字節(jié)碼對象
*/
public static synchronized void print2() {
synchronized(PrinterTest.class){
System.out.print("無");
System.out.print("糖");
System.out.print("口");
System.out.print("香");
System.out.print("糖");
System.out.print("\r\n");
}
}
public synchronized void print3() {
synchronized(this){
System.out.print("金");
System.out.print("鋼");
System.out.print("狼");
System.out.print("\r\n");
}
}
}
死鎖
多線程同步的時(shí)候, 如果同步代碼嵌套, 使用相同鎖, 就有可能出現(xiàn)死鎖趁仙。單例
保證類在內(nèi)存中只有一個(gè)對象。餓漢式
/**
* 單例模式的實(shí)現(xiàn):餓漢式,線程安全 但效率比較低
*/
public class SingletonTest {
// 定義一個(gè)私有的構(gòu)造方法
private SingletonTest() {
}
// 將自身的實(shí)例對象設(shè)置為一個(gè)屬性,并加上Static和final修飾符
private static final SingletonTest instance = new SingletonTest();
// 靜態(tài)方法返回該類的實(shí)例
public static SingletonTest getInstancei() {
return instance;
}
}
- 懶漢式
/**
* 單例模式的實(shí)現(xiàn):懶漢式,線程安全
*/
public class SingletonTest {
// 定義私有構(gòu)造方法(防止通過 new SingletonTest()去實(shí)例化)
private SingletonTest() {
}
// 定義一個(gè)SingletonTest類型的變量(不初始化垦页,注意這里沒有使用final關(guān)鍵字)
private static SingletonTest instance;
// 定義一個(gè)靜態(tài)的方法(調(diào)用時(shí)再初始化SingletonTest雀费,使用synchronized 避免多線程訪問時(shí),可能造成重的復(fù)初始化問題)
public static synchronized SingletonTest getInstance() {
if (instance == null)
instance = new SingletonTest();
return instance;
}
}
- 靜態(tài)內(nèi)部類法
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){
}
public static Singleton getSingleton(){
return SingletonHolder.instance;
}
}
- 雙重檢查鎖
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null){
synchronized(Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
- 枚舉法
public enum Singleton {
INSTACE;
private String name;
public String getName() {
return name;
}
}
- Timer
public class TimerDemo {
public static void main (String[] args) {
// 3秒后執(zhí)行
new Timer().schedule(new MyTimerTask(), 3000);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("匿名內(nèi)部類方式");
}
},2000);
// 周期執(zhí)行
// 延遲3秒痊焊,每1秒執(zhí)行一次
new Timer().schedule(new MyTimerTask(), 3000, 1000);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("每隔1秒定時(shí)任務(wù)");
}
},0,1000);
}
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
System.out.println("Timer test");
}
}
- 線程通信
wait():執(zhí)行該方法線程對象釋放同步鎖坐儿,JVM把該線程存放到等待池中律胀,等待其他線程喚醒該線程。
notify():執(zhí)行該方法的線程喚醒在等待池中等待的任一線程貌矿,把線程轉(zhuǎn)到鎖池中等待炭菌。
notifyAll():執(zhí)行該方法的線程喚醒在等待池中等待的所有線程,把線程轉(zhuǎn)到鎖池中等待逛漫。
這兩個(gè)方法必須在同步代碼中執(zhí)行, 并且使用同步鎖對象來調(diào)用黑低。
public class ThreadCommunicationDemo {
public static void main(String[] args){
MyPrinter myPrinter = new MyPrinter();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
myPrinter.print1();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
myPrinter.print2();
}
}
}).start();
// 三個(gè)線程通信
new Thread(new Runnable() {
@Override
public void run() {
while (true){
myPrinter.print3();
}
}
}).start();
}
}
class MyPrinter {
private int flag = 1;
public void print1() {
synchronized(this){ //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對象
// 兩個(gè)線程通信
/*if (flag != 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
while (flag != 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("你");
System.out.print("是");
System.out.print("豬");
System.out.print("嗎");
System.out.print("\r\n");
flag = 2;
// 兩個(gè)線程通信
// this.notify(); // 隨機(jī)喚醒單個(gè)線程
// 三個(gè)線程通信
this.notifyAll();
}
}
public synchronized void print2() {
synchronized(this){
/*if (flag != 2){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
while (flag != 2){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("無");
System.out.print("糖");
System.out.print("口");
System.out.print("香");
System.out.print("糖");
System.out.print("\r\n");
// 兩個(gè)線程通信
// flag = 1;
// this.notify();
// 三個(gè)線程通信
flag = 3;
this.notifyAll();
}
}
public synchronized void print3() {
synchronized(this){
while (flag != 3){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("金");
System.out.print("鋼");
System.out.print("狼");
System.out.print("\r\n");
flag = 1;
this.notifyAll();
}
}
}
如果多個(gè)線程之間通信, 需要使用notifyAll()通知所有線程, 用while來反復(fù)判斷條件。
注意事項(xiàng):
1.在同步代碼塊中用哪個(gè)對象鎖酌毡,就用哪個(gè)對象調(diào)用wait 方法克握。
2.sleep方法必須傳入?yún)?shù),參數(shù)就是時(shí)間枷踏,時(shí)間到了自動醒來菩暗,
wait 方法可以傳入?yún)?shù)也可以不傳入?yún)?shù)。
3.sleep方法在同步函數(shù)或同步代碼塊中旭蠕,不釋放鎖停团;
wait 方法在同步函數(shù)或同步代碼塊中,釋放鎖掏熬。
- 互斥鎖
1)使用Lock機(jī)制取代synchronized代碼塊和synchronized方法佑稠。
2)使用Condition接口對象的await()和signalAll()方法等待和喚醒線程。
1.同步
使用ReentrantLock類的lock()和unlock()方法進(jìn)行同步
2.通信
使用ReentrantLock 類的 newCondition() 方法可以獲取Condition對象旗芬;
需要等待的時(shí)候使用 Condition的 await() 方法, 喚醒的時(shí)候用 signal() 方法舌胶;
不同的線程使用不同的Condition, 這樣就能區(qū)分喚醒的時(shí)候找哪個(gè)線程了;
public class ReentrantLockDemo {
public static void main(String[] args) {
final MyPrinter1 myPrinter = new MyPrinter1();
new Thread() {
public void run() {
while(true) {
myPrinter.print1();
}
}
}.start();
new Thread() {
public void run() {
while(true) {
myPrinter.print2();
}
}
}.start();
new Thread() {
public void run() {
while(true) {
myPrinter.print3();
}
}
}.start();
}
}
class MyPrinter1 {
private ReentrantLock r = new ReentrantLock();
private Condition c1 = r.newCondition();
private Condition c2 = r.newCondition();
private Condition c3 = r.newCondition();
private int flag = 1;
public void print1() {
r.lock(); // 獲取鎖
if(flag != 1) {
try {
c1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("你");
System.out.print("是");
System.out.print("豬");
System.out.print("嗎");
System.out.print("\r\n");
flag = 2;
c2.signal();
r.unlock(); // 釋放鎖
}
public void print2() {
r.lock();
if(flag != 2) {
try {
c2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("金");
System.out.print("鋼");
System.out.print("狼");
System.out.print("\r\n");
flag = 3;
c3.signal();
r.unlock();
}
public void print3() {
r.lock();
if(flag != 3) {
try {
c3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("無");
System.out.print("糖");
System.out.print("口");
System.out.print("香");
System.out.print("糖");
System.out.print("\r\n");
flag = 1;
c1.signal();
r.unlock();
}
}
-
線程組(了解)
Java中使用ThreadGroup來表示線程組疮丛,它可以對一批線程進(jìn)行分類管理幔嫂,Java允許程序直接對線程組進(jìn)行控制。
默認(rèn)情況下誊薄,所有的線程都屬于主線程組履恩。
public final ThreadGroup getThreadGroup() // 通過線程對象獲取它所屬于的組
public final String getName() // 通過線程組對象獲取他組的名字
也可以給線程設(shè)置分組
1.ThreadGroup(String name) 創(chuàng)建線程組對象并給其賦值名字
2.創(chuàng)建線程對象
3.Thread(ThreadGroup?group, Runnable?target, String?name)
4.設(shè)置整組的優(yōu)先級或者守護(hù)線程
public class ThreadGroupDemo {
public static void main(String[] args) {
threadGroup();
myThreadGroup();
}
public static void threadGroup() {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr, "張三");
Thread t2 = new Thread(mr, "李四");
// 獲取線程組
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
String name1 = tg1.getName();
String name2 = tg2.getName();
System.out.println(name1);
System.out.println(name2);
// 線程默認(rèn)情況下屬于main線程組
// 默任情況下,所有的線程都屬于同一個(gè)組
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
public static void myThreadGroup(){
ThreadGroup tg = new ThreadGroup("我是一個(gè)新的線程組"); //創(chuàng)建新的線程組
MyRunnable mr = new MyRunnable(); //創(chuàng)建Runnable的子類對象
Thread t1 = new Thread(tg, mr, "張三"); //將線程t1放在組中
Thread t2 = new Thread(tg, mr, "李四"); //將線程t2放在組中
System.out.println(t1.getThreadGroup().getName()); //獲取組名
System.out.println(t2.getThreadGroup().getName());
// 通過組名稱設(shè)置后臺線程暇屋,表示該組的線程都是后臺線程
tg.setDaemon(true);
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "...." + i);
}
}
}
-
線程的生命周期
線程的生命周期
1似袁、新建狀態(tài)(new)
使用 new 關(guān)鍵字和 Thread 類或其子類建立一個(gè)線程對象后,該線程對象就處于新建狀態(tài)咐刨。它保持這個(gè)狀態(tài)直到程序 start() 這個(gè)線程昙衅。
2、就緒狀態(tài)(ready)
當(dāng)線程對象調(diào)用了start()方法之后定鸟,該線程就進(jìn)入就緒狀態(tài)而涉。就緒狀態(tài)的線程處于就緒隊(duì)列中,要等待JVM里線程調(diào)度器的調(diào)度联予。
3啼县、運(yùn)行狀態(tài)(running)
如果就緒狀態(tài)的線程獲取 CPU 資源材原,就可以執(zhí)行 run(),此時(shí)線程便處于運(yùn)行狀態(tài)季眷。處于運(yùn)行狀態(tài)的線程最為復(fù)雜余蟹,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)子刮。
4威酒、阻塞狀態(tài)(blocked)
如果一個(gè)線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法挺峡,失去所占用資源之后葵孤,該線程就從運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)。在睡眠時(shí)間已到或獲得設(shè)備資源后可以重新進(jìn)入就緒狀態(tài)橱赠∮热裕可以分為三種:
等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行 wait() 方法,使線程進(jìn)入到等待阻塞狀態(tài)狭姨。
同步阻塞:線程在獲取 synchronized 同步鎖失敗(因?yàn)橥芥i被其他線程占用)宰啦。
其他阻塞:通過調(diào)用線程的 sleep() 或 join() 發(fā)出了 I/O 請求時(shí),線程就會進(jìn)入到阻塞狀態(tài)送挑。當(dāng)sleep() 狀態(tài)超時(shí)绑莺,join() 等待線程終止或超時(shí)暖眼,或者 I/O 處理完畢惕耕,線程重新轉(zhuǎn)入就緒狀態(tài)。
阻塞狀態(tài)只能先進(jìn)入就緒狀態(tài)诫肠,不能直接進(jìn)入運(yùn)行狀態(tài)司澎。
5、死亡狀態(tài)(terminated)
一個(gè)運(yùn)行狀態(tài)的線程完成任務(wù)或者其他終止條件發(fā)生時(shí)栋豫,該線程就切換到終止?fàn)顟B(tài)挤安。
-
線程池(了解)
程序啟動一個(gè)新線程成本是比較高的,因?yàn)樗婕暗揭c操作系統(tǒng)進(jìn)行交互丧鸯。而使用線程池可以很好的提高性能蛤铜,尤其是當(dāng)程序中要創(chuàng)建大量生存期很短的線程時(shí),更應(yīng)該考慮使用線程池丛肢。線程池里的每一個(gè)線程代碼結(jié)束后围肥,并不會死亡,而是再次回到線程池中成為空閑狀態(tài)蜂怎,等待下一個(gè)對象來使用穆刻。
/*JDK5新增了一個(gè)Executors工廠類來產(chǎn)生線程池,有如下幾個(gè)方法
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
這些方法的返回值是ExecutorService對象杠步,該對象表示一個(gè)線程池氢伟,可以執(zhí)行Runnable對象或者Callable對象代表的線程榜轿。
*/
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以執(zhí)行Runnable對象或者Callable對象代表的線程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//結(jié)束線程池
pool.shutdown();
-
工廠模式
工廠方法模式中抽象工廠類負(fù)責(zé)定義創(chuàng)建對象的接口,具體對象的創(chuàng)建工作由繼承抽象工廠的具體類實(shí)現(xiàn)朵锣。
客戶端不需要在負(fù)責(zé)對象的創(chuàng)建谬盐,從而明確了各個(gè)類的職責(zé),如果有新的對象增加诚些,只需要增加一個(gè)具體的類和具體的工廠類即可设褐,不影響已有的代碼,后期維護(hù)容易泣刹,增強(qiáng)了系統(tǒng)的擴(kuò)展性助析。
// 動物抽象類
public abstract class Animal {
public abstract void eat();
}
// 工廠接口
public interface Factory {
public Animal createAnimal();
}
// 具體狗類
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨頭");
}
}
// 具體貓類
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃魚");
}
}
// 狗工廠
public class DogFactory implements Factory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
// 貓工廠
public class CatFactory implements Factory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
// 使用
public class Test {
public static void main(String[] args) {
DogFactory df = new DogFactory();
Dog d = (Dog) df.createAnimal();
d.eat();
}
}