07-Java基礎(chǔ)-多線程 & 單例

多線程新蟆、單例

  • 線程
    線程是程序執(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();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市椅您,隨后出現(xiàn)的幾起案子外冀,更是在濱河造成了極大的恐慌,老刑警劉巖掀泳,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雪隧,死亡現(xiàn)場離奇詭異,居然都是意外死亡员舵,警方通過查閱死者的電腦和手機(jī)脑沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來马僻,“玉大人庄拇,你說我怎么就攤上這事【碌耍” “怎么了措近?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長女淑。 經(jīng)常有香客問我瞭郑,道長,這世上最難降的妖魔是什么鸭你? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任屈张,我火速辦了婚禮,結(jié)果婚禮上袱巨,老公的妹妹穿的比我還像新娘阁谆。我一直安慰自己,他們只是感情好瓣窄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布笛厦。 她就那樣靜靜地躺著,像睡著了一般俺夕。 火紅的嫁衣襯著肌膚如雪裳凸。 梳的紋絲不亂的頭發(fā)上贱鄙,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音姨谷,去河邊找鬼逗宁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梦湘,可吹牛的內(nèi)容都是我干的瞎颗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捌议,長吁一口氣:“原來是場噩夢啊……” “哼哼拔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓣颅,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤倦逐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宫补,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檬姥,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年粉怕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了健民。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贫贝,死狀恐怖秉犹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情平酿,我是刑警寧澤凤优,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布悦陋,位于F島的核電站蜈彼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏俺驶。R本人自食惡果不足惜幸逆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望暮现。 院中可真熱鬧还绘,春花似錦、人聲如沸栖袋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塘幅。三九已至昔案,卻和暖如春尿贫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踏揣。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工庆亡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捞稿。 一個(gè)月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓又谋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親娱局。 傳聞我的和親對象是個(gè)殘疾皇子彰亥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容