一 案例引申
編寫代碼同時只允許五個線程并發訪問(以下文的函數為例子)
private static void method() {
System.out.println("ThreadName" + Thread.currentThread().getName() + "進來了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
思考:
1 若果使用 synchronized的話避免了并發安全問題,但是不滿足題目的要求,因為題目要求一次讓五個線程訪問,使用鎖的時候一次只能訪問一個。
2 解決思路:使用信號量或者使用線程池
二 信號量的簡單使用(了解)
package ThreadPool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Create by SunnyDay on 2018/11/27
*/
public class ThreadPoolDemo {
//信號量 Doug Lea 大牛寫的(concurrent并發包下面的東西都是他寫的) 參數控制并發線程的個數
private static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
}
}
}).start();
}
private static void method() throws InterruptedException {
// 首先獲取一把鎖
semaphore.acquire();
System.out.println("ThreadName" + Thread.currentThread().getName() + "進來了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
// 釋放鎖
semaphore.release();
}
三 使用線程池解決(了解)
package ThreadPool;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Create by SunnyDay on 2018/11/27
* 使用線程池解決
*/
public class ThreadPool {
/**
* java 提供了線程池的創建工具類( Executors),
* 我們可以通過工具類創建不同的線程池
*/
private static Executor executor = Executors.newCachedThreadPool();//緩沖線程池
private static Executor executor2 = Executors.newFixedThreadPool(5);//固定線程池
private static Executor executor3 = Executors.newScheduledThreadPool(5);//計劃任務線程池
private static Executor executor4 = Executors.newSingleThreadExecutor();//單個線程池(池中只有一個線程)
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
// 線程池的簡單使用(十分簡單) 調用execute方法傳遞參數類型為runnable類型即可
executor2.execute(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
private static void method() throws InterruptedException {
System.out.println("ThreadName" + Thread.currentThread().getName() + "進來了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
}
四 自定義線程池
1 小發現
/**
* 查看隨便幾個個線程池的源碼:
* public static ExecutorService newCachedThreadPool() {
* return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
* 60L, TimeUnit.SECONDS,
* new SynchronousQueue());
* }
*
* 發現都是對線程池類(ThreadPoolExecutor)進行了簡單的封裝(傳的參數不一致)
*/
2 ThreadPoolExecutor構造( 以6個參數的為例子)
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活時間 long(任務完成后要銷毀的延時)
* @param unit 時間單位 決定參數3的單位,枚舉類型的時間單位
* @param workQueue 工作隊列 用于存儲任務的工作隊列(BlockingQueue接口類型)
* @param threadFactory 線程工廠 用于創建線程
*
* */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
3 自定義實現案例
package ThreadPool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Create by SunnyDay on 2018/11/27
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
/**手寫線程池
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活時間 long(任務完成后要銷毀的延時)
* @param unit 時間單位 決定參數3的單位,枚舉類型的時間單位
* @param workQueue 工作隊列 用于存儲任務的工作隊列(BlockingQueue接口類型)
* @param threadFactory 線程工廠 用于創建線程
*
*線程不是越多越好,google工程師推薦 線程個數=cpu核心數+1(例如四核的開5個線程最好)
* */
// 參數任務上限
LinkedBlockingQueue blockingQueue = new LinkedBlockingQueue<>(100);
ThreadFactory threadFactory = new ThreadFactory() {
// int i = 0; 用并發安全的包裝類
AtomicInteger atomicInteger = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
//創建線程 吧任務傳進來
Thread thread = new Thread(r);
// 給線程起個名字
thread.setName("MyThread" + atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 1, TimeUnit.SECONDS, blockingQueue, threadFactory);
for (int i = 0; i < 100; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/**
* 編寫代碼(如下文的函數)同時只允許五個線程并發訪問:
*
* 1 若果使用 synchronized的話避免了并發安全問題,但是不滿足題目的要求,
* 因為題目要求一次讓五個線程訪問,使用鎖的時候一次只能訪問一個。
* 2 解決思路:使用信號量或者使用線程池
*
* 3 自己手動封裝線程池(明白啟動策略)
*/
private static void method() throws InterruptedException {
System.out.println("ThreadName" + Thread.currentThread().getName() + "進來了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
}
五 線程池補充
1 啟動策略草圖
image.png
2 流程圖(摘抄)
image.png
六 總結
通過上面的總結相信對線程池的概念,使用,啟動策略,大致的有了一些了解,但是這些還是不夠的,想要深入了解還需我們不懈的努力探討!!!