線程基礎知識歸納

1. 進程與線程的區別

  • 進程:是系統進行分配和管理資源的基本單位

  • 線程:進程的一個執行單元,是進程內調度的實體、是CPU調度和分派的基本單位,是比進程更小的獨立運

    行的基本單位。線程也被稱爲輕量級進程,線程是程序執行的最小單位。

  • 一個程序至少一個進程,一個進程至少一個線程。

  • 進程有自己的獨立地址空間,每啓動一個進程,系統就會爲它分配地址空間,建立數據表來維護代碼段、堆棧

    段和數據段,這種操作非常昂貴。 而線程是共享進程中的數據的,使用相同的地址空間,因此CPU切換一個
    線程的花費遠比進程要小很多,同時創建一個線程的開銷也比進程要小很多。 線程之間的通信更方便,同一
    進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信需要以通信的方式進行。 如何處理好同步
    與互斥是編寫多線程程序的難點。 多進程程序更健壯,進程有獨立的地址空間,一個進程崩潰後,在保護模
    式下不會對其它進程產生影響, 而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但
    線程之間沒有單獨的地址空間,所以可能一個線程出現問題,進而導致整個程序出現問題

2.線程的狀態及其相互轉換

初始(NEW):新創建了一個線程對象,但還沒有調用start()方法。
運行(RUNNABLE):處於可運行狀態的線程正在JVM中執行,但它可能正在等待來自操作系統的其他資源,例
如處理器。
阻塞(BLOCKED):線程阻塞於synchronized鎖,等待獲取synchronized鎖的狀態。
等待(WAITING):Object.wait()、join()、 LockSupport.park(),進入該狀態的線程需要等待其他線程做出一些特
定動作(通知或中斷)。
超時等待(TIME_WAITING):Object.wait(long)、Thread.join()、LockSupport.parkNanos()、
LockSupport.parkUntil,該狀態不同於WAITING,它可以在指定的時間內自行返回。
終止(TERMINATED):表示該線程已經執行完畢。

2.1創建線程的方式(上)

繼承Thread,並重寫父類的run方法
實現Runable接口,並實現run方法
實際開發中,選第2種:java只允許單繼承 增加程序的健壯性,代碼可以共享,代碼跟數據獨立

2.2創建線程的方式(下)

使用匿名內部類
Lambda表達式
線程池

線程阻塞Demo:
public class ThreadStateDemo {

    public static void main(String[] args) throws InterruptedException {


        /**
         * Block
         */

//        Object obj = new Object();
//        Thread thread = new Thread(()->{
//
//            synchronized (obj){
//
//                try {
//                    Thread.sleep(100000000L);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
//
//        });
//
//        thread.start();
//
//        Thread.sleep(2000L);
//
//        Thread thread2 = new Thread(()->{
//
//            synchronized (obj){
//
//            }
//
//        });
//        thread2.start();
//
//    }

/**
 * waiting
 */
        Object obj = new Object();
        Thread thread = new Thread(() -> {

            synchronized (obj) {

                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        thread.start();
    }
}

線程池開啓單線程:
public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
}
相應的圖示如下:

3.線程的掛起跟恢復

​ 什麼是掛起線程? 線程的掛起操作實質上就是使線程進入“非可執行”狀態下,在這個狀態下CPU不會分給線程
時間片,進入這個狀態可以用來暫停一個線程的運行。 在線程掛起後,可以通過重新喚醒線程來使之恢復運

3.1 爲什麼要掛起線程?

​ cpu分配的時間片非常短、同時也非常珍貴。避免資源的浪費。
如何掛起線程?

  • 被廢棄的方法 thread.suspend() 該方法不會釋放線程所佔用的資源。如果使用該方法將某個線程掛起,則可

    能會使其他等待資源的線程死鎖 thread.resume() 方法本身並無問題,但是不能獨立於suspend()方法存在

  • 可以使用的方法 wait() 暫停執行、放棄已經獲得的鎖、進入等待狀態

  • notify() 隨機喚醒一個在等待鎖的線程

  • notifyAll() 喚醒所有在等待鎖的線程,自行搶佔cpu資源

被廢棄的相關掛起Demo:

/**
 * 掛起操作的Demo
 */
public class SuspendDemo implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"進入Run()方法,準備調用SusPend");
        Thread.currentThread().suspend(); //執行掛起
        System.out.println(Thread.currentThread().getName()+"進入Run()方法,調用SusPend結束");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new SuspendDemo());
        thread.start();
        Thread.sleep(3000L);
        thread.resume(); //執行恢復
    }
}

這裏要注意的是掛起的操作是不安全的,被廢棄也是因爲因爲他容易造成長時間阻塞:

這裏阻塞的原因是因爲他在掛起前就進行來了喚醒的操作,導致後來的線程一直掛起(未睡眠導致喚醒的代碼提前於掛起執行了)

**
 * suspend死鎖演示
 */
public class DeadDemo implements Runnable{

    private static final Object object =new Object();
    @Override
    public void run() {
        //持有資源
        synchronized (object){

            System.out.println(Thread.currentThread().getName()+"佔用資源");

            Thread.currentThread().suspend();
        }
        System.out.println(Thread.currentThread().getName()+"釋放資源");
    }


    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new DeadDemo(),"對比線程");
        thread.start();
        thread.sleep(1000L);
        thread.resume();

        Thread deadThread = new Thread(new DeadDemo(),"死鎖線程");
        deadThread.start();
//        deadThread.sleep(3000L);
        deadThread.resume();

    }
}

使用推薦的wait和notify方法操作:

package xianchengxuexi.chapter02.hang;

import com.sun.jndi.toolkit.ctx.StringHeadTail;

/**
 * 等待方法
 */
public class WaitDemo implements Runnable {

    private static Object object =  new Object();
    private static Object waitObj = new Object();

    @Override
    public void run() {
        //持有資源
        //鎖住的和wait()的需要時同一個對象
        synchronized (waitObj){
            System.out.println(Thread.currentThread().getName()+"佔用資源");
            try {
                waitObj.wait();//這個會釋放鎖
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        System.out.println(Thread.currentThread().getName()+"釋放資源");

    }


    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new WaitDemo(),"對比線程");
        thread.start();

        Thread thread2 = new Thread(new DeadDemo(),"對比線程2");
        thread2.start();
        thread.sleep(3000L);

        synchronized (waitObj){
            waitObj.notify();
        }


    }
}
3.2 什麼時候適合使用掛起線程?

我等的船還不來(等待某些未就緒的資源),我等的人還不明白。直到notify方法被調用

3.3線程的中斷操作
stop() 廢棄方法,開發中不要使用。因爲一調用,線程就立刻停止,此時有可能引發相應的線程安全性問題
/**
 * 不安全的stop
 */
public class UnSafeWithStop extends Thread {

    private int a =0;
    private int b =0;

    @Override
    public void run() {
        a++;
        try {
            sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        b++;
    }

    public  void printf(){
        System.out.println("a的值=======>"+a);
        System.out.println("b的值=======>"+b);
    }

    public static void main(String[] args) throws InterruptedException {
        UnSafeWithStop unSafeWithStop = new UnSafeWithStop();
        unSafeWithStop.start();
        Thread.sleep(1000L);
        unSafeWithStop.stop();
        unSafeWithStop.printf();

    }

}

Thread.interrupt方法
/**
 * 使用Interrupt終止線程
 */
public class InterruptDemo implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println(Thread.currentThread().getName());
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new InterruptDemo());
        thread.start();
        thread.sleep(1000L);
        thread.interrupt();

    }
}

自行定義一個標誌,用來判斷是否繼續執行
/**
 * 自定義線程終止
 */
public class MyInterruptDemo implements Runnable{

    private static volatile boolean FLAG = true;
    @Override
    public void run() {
        while (FLAG){
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new MyInterruptDemo());
        thread.start();
        Thread.sleep(1000L);
        FLAG=false;
    }
}

4.線程的優先級

​ 線程的優先級告訴程序該線程的重要程度有多大。如果有大量線程都被堵塞,都在等候運行,程序會儘可能地先運行優先級的那個線程。

​ 但是,這並不表示優先級較低的線程不會運行。若線程的優先級較低,只不過表示它被准許運行的機會小一些而已。
線程的優先級設置可以爲1-10的任一數值,Thread類中定義了三個線程優先級,分別是:
MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10),

​ 一般情況下推薦使用這幾個常量,不要自行設置數值。

/**
 * 線程優先級Demo
 */
public class ProrityDemo {

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true){
                System.out.println(Thread.currentThread().getName());
            }
        },"線程1");
        Thread thread2 = new Thread(() -> {
            while (true){
                System.out.println(Thread.currentThread().getName());

            }        },"線程2");
        thread.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.MAX_PRIORITY);

        thread.start();
        thread2.start();
    }

}

不同平臺,對線程的優先級的支持不同。 編程的時候,不要過度依賴線程優先級,如果你的程序運行是否正
確取決於你設置的優先級是否按所設置的優先級運行,那這樣的程序不正確
任務:
快速處理:設置高的優先級 慢慢處理:設置低的優先級

5.守護線程

線程分類
用戶線程、守護線程 守護線程:任何一個守護線程都是整個程序中所有用戶線程的守護者,只要有活着的用
戶線程,守護線程就活着。當JVM實例中最後一個非守護線程結束時,也隨JVM一起退出
守護線程的用處:jvm垃圾清理線程
建議: 儘量少使用守護線程,因其不可控不要在守護線程裏去進行讀寫操作、執行計算邏輯

/**
 * 守護線程Demo
 */
public class DaemonThreadDemo implements Runnable{
    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new DaemonThreadDemo());
        thread.start();
        thread.setDaemon(true);
        Thread.sleep(2000L);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章