JAVA多線程——(一)多線程編程

JAVA多線程——多線程編程

【一】線程創建

  • Thread創建:

如果要使用Thread來創建線程,需要新建一個類來繼承Thread,重新run方法

class MyThread extends Thread {
    
    /**
     * 這是線程任務 
     */
    @Override
    public void run() {
        System.out.println("這是新建線程");
    }
}

public class Main {
    public static void main(String args[]){
        //創建線程
       MyThread myThread = new MyThread();
       //執行線程任務
       myThread.start();
    }
}
  • Runnable
    使用runnable需要寫一個實現這個接口的類,但是這種方法是沒返回值的
class MyTask implements Runnable {

    /**
     * 這是線程任務
     */
    @Override
    public void run() {
        System.out.println("這是新建線程");
    }
}

public class Main {
    public static void main(String args[]) {
        //創建任務
        MyTask task = new MyTask();
        //創建線程
        Thread thread = new Thread(task);
        
        //執行線程任務
        thread.start();
    }
}

【二】線程狀態轉換

在這裏插入圖片描述

  • 1、新建狀態(New):新創建了一個線程對象。

  • 2、就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。

  • 3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

  • 4、阻塞狀態(Blocked):阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態

  • 5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

詳細線程狀態轉換請移步到博客:https://blog.csdn.net/wenge1477/article/details/90481125

【三】守護線程

只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就全部工作;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工作。Daemon的作用是爲其他線程的運行提供便利服務,守護線程最典型的應用就是 GC (垃圾回收器),它就是一個很稱職的守護者。

java中線程包括兩種:
1、User Thread(用戶線程)
2、Daemon Thread(守護線程)

class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("這是新建線程");
    }
    
}

public class Main {
    public static void main(String args[]) {
        //創建任務
        MyTask task = new MyTask();
        //創建線程
        Thread thread = new Thread(task);

        //設置爲守護線程
        thread.setDaemon(true);

        //執行線程任務
        thread.start();
    }
}

  • // 設定 daemonThread 爲 守護線程,default false(非守護線程)
 daemonThread.setDaemon(true);  
  • // 驗證當前線程是否爲守護線程,返回 true 則爲守護線程
 daemonThread.isDaemon();  

【四】線程同步

即當有一個線程在對內存進行操作時,其他線程都不可以對這個內存地址進行操作,直到該線程完成操作, 其他線程才能對該內存地址進行操作,而其他線程又處於等待狀態,實現線程同步的方法有很多,臨界區對象就是其中一種

  • synchronied
    synchronied進行對象鎖,鎖住對象,
  • ReentranLock
  • ReadWriteLock
  • wait和notify
  • condition
  • 阻塞隊列
  • CountDownLatch
  • CyclicBarrier

【五】線程死鎖

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。各自持有資源不釋放,確申請獲取對方手中的資源,形成相互等待

  • 互斥條件:一個資源每次只能被一個進程使用。

  • 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。

  • 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。

  • 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。

死鎖例子:

public class DeadLockSample extends Thread{
    private String first;
    private String second;
    public DeadLockSample (String name,String first,String second){
        super(name);
        this.first = first;
        this.second = second;
    }

    public void run(){
        synchronized (first){
            System.out.println(this.getName() + " obtained:" + first);

            try {
                Thread.sleep(1000L);
                synchronized (second){
                    System.out.println(this.getName() +" obtained: " + second);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        String lockA = "lockA";
        String lockB = "lockB";
        DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
        DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

    }

}
結果:
Thread1 obtained:lockA
Thread2 obtained:lockB
或者
Thread2 obtained:lockB
Thread1 obtained:lockA


預防死鎖的幾種方法:

  • 加鎖順序(線程按照一定的順序加鎖)
    如果能確保所有的線程都是按照相同的順序獲得鎖,那麼死鎖就不會發生

  • 加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)
    在申請資源的時候設置時間限制,當時間到了就不在申請資源,比如lock的tryLocak方法可以設置時間

  • 死鎖檢測

【六】synchronied

synchronized關鍵字是java併發編程中必不可少的工具,synchronized是圍繞一個被稱爲內部鎖或監視鎖的內部實體實現的(Api規範裏經常將其簡單的稱之爲“monitor”)。內部鎖在同步的兩個方面發揮作用:強制獨佔訪問對象狀態和建立對可見性必不可少的happens-before關係

  • 同步代碼塊
    鎖住的是一個對象
1、第一種
 public void getCunt(){
        synchronized (this){
            System.out.println("這是同步代碼塊!!!");
        }
}

2、第二種
 public synchronized void getCunt() {
        System.out.println("這是同步代碼塊!!!");
}
  • 同步方法
    鎖住的是當前的類的class
1、第一種
public void getCunt() {
        synchronized (Main.class) {
            System.out.println("這是同步代碼塊!!!");
        }
}

2、第二種
public static synchronized void getCunt() {
        System.out.println("這是同步代碼塊!!!");
    }

【七】wait和notify

public class Main {
    public static void main(String args[]){
        String lock = new String();

        Stack<Integer> stack = new Stack<>();
        AtomicInteger i = new AtomicInteger(1);

        //生產者生產數據
        Thread producer = new Thread(() -> {
            synchronized (lock) {
                while (stack.isEmpty()) {
                    stack.push(new Integer(i.getAndIncrement()));
                    lock.notifyAll();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        //消費者消費數據
        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                while (!stack.isEmpty()) {
                    System.out.print(stack.pop()+" ");
                    lock.notifyAll();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        producer.start();
        consumer.start();
    }
}


【八】鏈接

https://blog.csdn.net/J080624/article/details/82721827
https://www.jianshu.com/p/f2c91afe6266

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