線程池-4

一、什麼是線程池?

線程池就是在線程使用之前先把線程創建出來,把線程放在一個池子裏面養着,等需要用的時候直接使用,任務完成之後再把線程歸還。

二、線程池有哪些作用

第一:提供響應效率,有了線程池,當有任務的時候,就有線程直接去完成任務,不用調用系統的資源去新建線程

第二:節約系統資源,線程池中的線程是可以回收重複利用的,降低新建和銷燬線程的資源消耗。

第三:提高線程的管理性,我們知道線程是稀缺資源,不能一直無限制的創建,通過使用線程池我們可以一分配、調優

三、線程池的創建方式

Java是天生就支持併發的語言,支持併發意味着多線程,線程的頻繁創建在高併發及大數據量是非常消耗資源的,因爲java提供了線程池。在jdk1.5以前的版本中,線程池的使用是及其簡陋的,但是在JDK1.5後,有了很大的改善。JDK1.5之後加入了java.util.concurrent包,java.util.concurrent包的加入給予開發人員開發併發程序以及解決併發問題很大的幫助。這篇文章主要介紹下併發包下的Executor接口,Executor接口雖然作爲一個非常舊的接口(JDK1.5 2004年發佈),但是很多程序員對於其中的一些原理還是不熟悉,因此寫這篇文章來介紹下Executor接口,同時鞏固下自己的知識。如果文章中有出現錯誤,歡迎大家指出。

Executor框架的最頂層實現是ThreadPoolExecutor類,Executors工廠類中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其實也只是ThreadPoolExecutor的構造函數參數不同而已。通過傳入不同的參數,就可以構造出適用於不同應用場景下的線程池,那麼它的底層原理是怎樣實現的呢,這篇就來介紹下ThreadPoolExecutor線程池的運行過程。

 

corePoolSize: 核心池的大小。 當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中
maximumPoolSize: 線程池最大線程數,它表示在線程池中最多能創建多少個線程;
keepAliveTime: 表示線程沒有任務執行時最多保持多久時間會終止。
unit: 參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:

線程池四種創建方式

Java通過Executors(jdk1.5併發包)提供四種線程池,分別爲:
newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

代碼演示:

創建可緩存的線程池:

package com.junlaninfo.CachedThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by 輝 on 2020/5/24.
 * 可緩存線程池
 */
public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0;i<100;i++){
            executorService.execute(new Runnable() {
                public void run() {
                    //打印出來線程的名字是重複的
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

創建定長線程池

package com.junlaninfo.FixedThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by 輝 on 2020/5/24.
 * 創建一個定長線程池
定長線程池的大小最好根據系統資源進行設置。如Runtime.getRuntime().availableProcessors()
 */
public class FixedThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i=0;i<10;i++){
            executorService.execute(new Runnable() {
                public void run() {
                    //通過打印線程的名字我們可以知道,只有三個線程
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

 

創建定時執行的線程池

package com.junlaninfo.ScheduledThreadPool;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by 輝 on 2020/5/24.
 * 定時執行的線程池
 */
public class ScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        for (int i=0;i<10;i++){
            scheduledExecutorService.schedule(new Runnable() {
                public void run() {
                 System.out.println("當前線程是:"+Thread.currentThread().getName());
                }
            },3, TimeUnit.SECONDS);

        }
    }
}

創建單一線程的線程池,遵循先進先出的原則,不會造成線程安全的問題

package com.junlaninfo.SingleThreadExecutor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by 輝 on 2020/5/24.
 * 創建一個單線程的線程池,可以保證執行順序
 */
public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            public void run() {
                for (int i=0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+"數字是:"+i);
                }
            }
        });


    }
}

四、重入鎖

鎖作爲併發共享數據,保證一致性的工具,在JAVA平臺有多種實現(如 synchronized 和 ReentrantLock等等 ) 。這些已經寫好提供的鎖爲我們開發提供了便利

重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之後 ,內層遞歸函數仍然有獲取該代碼的鎖,但不受影響。
在JAVA環境下 ReentrantLock 和synchronized 都是可重入鎖

 

代碼演示synchronized的可重入性

package com.junlaninfo.Reentrantlock;



/**
 * Created by 輝 on 2020/5/24.
 * 演示synchronized的可重入鎖
 * set方法和get方法需要this,這把鎖,當訪問get方法的時候,獲取到this鎖,同時get方法內訪問set方法,
 * 這時候也可以拿到this這把鎖,這就是鎖的可重入
 */
public class synchronizedtest implements Runnable {
    public synchronized void set() {
        System.out.println("我是set方法");

    }

    public synchronized void get() {
        set();
        System.out.println("我是get方法");
    }

    public void run() {
        get();
    }

    public static void main(String[] args) {
        synchronizedtest synchronizedtest = new synchronizedtest();
        new Thread(synchronizedtest).start();
        new Thread(synchronizedtest).start();
        new Thread(synchronizedtest).start();
    }
}

代碼演示lock鎖的可重入性

package com.junlaninfo.Reentrantlock;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by 輝 on 2020/5/24.
 * 測驗lock鎖的可重入性
 */
public class Locktest  implements   Runnable {
    ReentrantLock  lock= new  ReentrantLock();

    public void  set() throws InterruptedException {
        lock.lock();//上鎖
        Thread.sleep(2000);
        System.out.println("我是set方法");
        lock.unlock();
    }
    public void  get() throws InterruptedException {
        lock.lock();
        Thread.sleep(2000);
        set();
        System.out.println("我是get方法");
        lock.unlock();
    }

    public void run() {
        try {
            get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        Locktest locktest = new Locktest();
        new  Thread(locktest).start();
        new  Thread(locktest).start();
    }
}

 

五、讀寫鎖

相比Java中的鎖(Locks in Java)裏Lock實現,讀寫鎖更復雜一些。假設你的程序中涉及到對一些共享資源的讀和寫操作,且寫操作沒有讀操作那麼頻繁。在沒有寫操作的時候,兩個線程同時讀一個資源沒有任何問題,所以應該允許多個線程能在同時讀取共享資源。但是如果有一個線程想去寫這些共享資源,就不應該再有其它線程對該資源進行讀或寫(譯者注:也就是說:讀-讀能共存,讀-寫不能共存,寫-寫不能共存)。這就需要一個讀/寫鎖來解決這個問題。Java5在java.util.concurrent包中已經包含了讀寫鎖。

代碼演示:

package com.junlaninfo.ReadWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 /** * Created by 輝 on 2020/5/24.  */

public class Cache {
    static Map<String, Object> map = new HashMap<String, Object>();
   static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
   static Lock r = rwl.readLock();
   static Lock w = rwl.writeLock();

   // 獲取一個key對應的value
   public static final Object get(String key) {
      r.lock();
      try {
         System.out.println("正在做讀的操作,key:" + key + " 開始");
         Thread.sleep(100);
         Object object = map.get(key);
         System.out.println("正在做讀的操作,key:" + key + " 結束");
         System.out.println();
         return object;
      } catch (InterruptedException e) {

      } finally {
         r.unlock();
      }
      return key;
   }

   // 設置key對應的value,並返回舊有的value
   public static final Object put(String key, Object value) {
      w.lock();
      try {

         System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "開始.");
         Thread.sleep(100);
         Object object = map.put(key, value);
         System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "結束.");
         System.out.println();
         return object;
      } catch (InterruptedException e) {

      } finally {
         w.unlock();
      }
      return value;
   }

   // 清空所有的內容
   public static final void clear() {
      w.lock();
      try {
         map.clear();
      } finally {
         w.unlock();
      }
   }

   public static void main(String[] args) {
      new Thread(new Runnable() {

         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Cache.put(i + "", i + "");
            }

         }
      }).start();
      new Thread(new Runnable() {

         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Cache.get(i + "");
            }

         }
      }).start();
   }
}

 

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