11.線程池
先看一個簡單的實例
public class TestThreadPool {
public static void main(String[] args) {
ThreadPoolDemo td = new ThreadPoolDemo();
new Thread(td).start();
new Thread(td).start();
new Thread(td).start();
}
}
class ThreadPoolDemo implements Runnable{
private int i = 0;
@Override
public void run() {
while (i<= 100) {
System.out.println(Thread.currentThread().getName());
}
}
}
上面我們就是創(chuàng)建和銷毀了三個線程實例,這樣不斷的創(chuàng)建和銷毀,就會消耗資源,于是就出現(xiàn)了線程池缓苛,和數(shù)據(jù)庫連接池一樣芳撒,就是有許多的線程已經(jīng)創(chuàng)建好了邓深,我們直接使用就好。
線程池:提供了一個線程隊列笔刹,隊列中保存著所有等待狀態(tài)的線程芥备,避免了創(chuàng)建與銷毀額外的開銷,提高了響應(yīng)的性能舌菜。
線程池的體系結(jié)構(gòu)
java.util.concurrent.Executor:負責(zé)線程的使用與調(diào)度的根接口
一個 ExecutorService萌壳,它使用可能的幾個池線程之一執(zhí)行每個提交的任務(wù),通常使用 Executors 工廠方法配置。
線程池可以解決兩個不同問題:由于減少了每個任務(wù)調(diào)用的開銷袱瓮,它們通崇凸牵可以在執(zhí)行大量異步任務(wù)時提供增強的性能,并且還可以提供綁定和管理資源(包括執(zhí)行任務(wù)集時使用的線程)的方法尺借。每個 ThreadPoolExecutor 還維護著一些基本的統(tǒng)計數(shù)據(jù)绊起,如完成的任務(wù)數(shù)。
工具類Executor
- ExecutorService newFixedThreadPool(int nThreads) 創(chuàng)建一個可重用固定線程數(shù)的線程池燎斩,以共享的無界隊列方式來運行這些線程虱歪。
- ExecutorService newCachedThreadPool() 創(chuàng)建一個可根據(jù)需要創(chuàng)建新線程的線程池
- static ExecutorService newSingleThreadExecutor() 創(chuàng)建一個使用單個 worker 線程的 Executor
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 創(chuàng)建一個線程池,它可安排在給定延遲后運行命令或者定期地執(zhí)行栅表。
線程池的使用方法如下:
public class TestThreadPool {
public static void main(String[] args) {
// 1.創(chuàng)建線程池
ExecutorService pool = Executors.newFixedThreadPool(5);//創(chuàng)建了5個線程的線程池
ThreadPoolDemo td = new ThreadPoolDemo();
// 2.為線程池中的線程分配任務(wù)
for (int i = 0; i < 10; i++) {
pool.submit(td);
}
// 3.關(guān)閉線程池
pool.shutdown();
}
}
class ThreadPoolDemo implements Runnable{
private int i = 0;
@Override
public void run() {
while (i<= 5) {
System.out.println(Thread.currentThread().getName()+":"+i++);
}
}
}
使用Callable創(chuàng)建線程的方式
public class TestThreadPool {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 1.創(chuàng)建線程池
ExecutorService pool = Executors.newFixedThreadPool(5);//創(chuàng)建了5個線程的線程池
List<Future<Integer>> list = new ArrayList<Future<Integer>>();
for (int i = 0; i < 10; i++) {
Future<Integer> future = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum +=i;
}
return sum;
}
});
list.add(future);
}
// 3.關(guān)閉線程池
pool.shutdown();
for (Future<Integer> future: list) {
System.out.println(future.get());
}
}
}
12.線程調(diào)度
在線程池中提到了一個ScheduledExecutorService這一個類就是用來實現(xiàn)線程的調(diào)度的笋鄙。
public class TestScheduledThread {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);// 創(chuàng)建五個線程的線程池
for (int i = 0; i < 5; i++) {
ScheduledFuture<Integer> result = pool.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(100);//產(chǎn)生隨機數(shù)
System.out.println(Thread.currentThread().getName()+":"+num);
return num;
}
}, 3, TimeUnit.SECONDS);
System.out.println(result.get());
}
pool.shutdown();
}
}
13.Fork/Join框架
Fork/Join框架就是在必要的情況下,將一個大任務(wù)怪瓶,進行拆分(fork)成若干個小任務(wù)(拆到不可再拆為止萧落,即臨界值),再將一個個的小任務(wù)運算的結(jié)果進行join匯總劳殖,這里要說明的是拆分和合并也需要時間铐尚,例如求100的累加和,可以使用for循環(huán)直接累加哆姻,也可以使用Fork/Join,但是執(zhí)行時間反而是for循環(huán)要快的多宣增,如果要計算100000000就是Fork/Join要快的多,所以包括臨界值的設(shè)定都要不停的測試得出矛缨,下面給出一個實例吧爹脾。
public class TestForkJoinPool {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinSum(0L, 1000000L);
long sum = pool.invoke(task);
System.out.println(sum);
}
}
class ForkJoinSum extends RecursiveTask<Long>{
private static final long serialVersionUID = 1L;
private long start;
private long end;
private static final long THURSHOLD = 10000L;// 臨界值
public ForkJoinSum(long start,long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end -start;
if(length <= THURSHOLD){
long sum = 0L;
for(long i = start; i<= end;i++){
sum +=i;
}
return sum;
}else{
long middle = (end+start) / 2;
ForkJoinSum left = new ForkJoinSum(start, middle);//遞歸操作
left.fork();// 進行拆分,同時壓入線程隊列
ForkJoinSum right = new ForkJoinSum(middle+1, end);
right.fork();
return left.join()+right.join();
}
}
}