激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java多線程優(yōu)化方法及使用方式

Java多線程優(yōu)化方法及使用方式

2021-03-31 13:40我心自在 Java教程

這篇文章主要介紹了Java多線程優(yōu)化方法及使用方式,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

一、多線程介紹

在編程中,我們不可逃避的會遇到多線程的編程問題,因?yàn)樵诖蠖鄶?shù)的業(yè)務(wù)系統(tǒng)中需要并發(fā)處理,如果是在并發(fā)的場景中,多線程就非常重要了。另外,我們在面試的時候,面試官通常也會問到我們關(guān)于多線程的問題,如:如何創(chuàng)建一個線程?我們通常會這么回答,主要有兩種方法,第一種:繼承thread類,重寫run方法;第二種:實(shí)現(xiàn)runnable接口,重寫run方法。那么面試官一定會問這兩種方法各自的優(yōu)缺點(diǎn)在哪,不管怎么樣,我們會得出一個結(jié)論,那就是使用方式二,因?yàn)槊嫦驅(qū)ο筇岢倮^承,盡量多用組合。

這個時候,我們還可能想到,如果想得到多線程的返回值怎么辦呢?根據(jù)我們多學(xué)到的知識,我們會想到實(shí)現(xiàn)callable接口,重寫call方法。那么多線程到底在實(shí)際項目中怎么使用呢,他有多少種方式呢?

首先,我們來看一個例子:

Java多線程優(yōu)化方法及使用方式

這是一種創(chuàng)建多線程的簡單方法,很容易理解,在例子中,根據(jù)不同的業(yè)務(wù)場景,我們可以在thread()里邊傳入不同的參數(shù)實(shí)現(xiàn)不同的業(yè)務(wù)邏輯,但是,這個方法創(chuàng)建多線程暴漏出來的問題就是反復(fù)創(chuàng)建線程,而且創(chuàng)建線程后還得銷毀,如果對并發(fā)場景要求低的情況下,這種方式貌似也可以,但是高并發(fā)的場景中,這種方式就不行了,因?yàn)閯?chuàng)建線程銷毀線程是非常耗資源的。所以根據(jù)經(jīng)驗(yàn),正確的做法是我們使用線程池技術(shù),jdk提供了多種線程池類型供我們選擇,具體方式可以查閱jdk的文檔。

Java多線程優(yōu)化方法及使用方式

這里代碼我們需要注意的是,傳入的參數(shù)代表我們配置的線程數(shù),是不是越多越好呢?肯定不是。因?yàn)槲覀冊谂渲镁€程數(shù)的時候要充分考慮服務(wù)器的性能,線程配置的多,服務(wù)器的性能未必就優(yōu)。通常,機(jī)器完成的計算是由線程數(shù)決定的,當(dāng)線程數(shù)到達(dá)峰值,就無法在進(jìn)行計算了。如果是耗cpu的業(yè)務(wù)邏輯(計算較多),線程數(shù)和核數(shù)一樣就到達(dá)峰值了,如果是耗i/o的業(yè)務(wù)邏輯(操作數(shù)據(jù)庫,文件上傳、下載等),線程數(shù)越多一定意義上有助于提升性能。

線程數(shù)大小的設(shè)定又一個公式?jīng)Q定:

y=n*((a+b)/a),其中,n:cpu核數(shù),a:線程執(zhí)行時程序的計算時間,b:線程執(zhí)行時,程序的阻塞時間。有了這個公式后,線程池的線程數(shù)配置就會有約束了,我們可以根據(jù)機(jī)器的實(shí)際情況靈活配置。

二、多線程優(yōu)化及性能比較

最近的項目中用到了所線程技術(shù),在使用過程中遇到了很多的麻煩,趁著熱度,整理一下幾種多線程框架的性能比較。目前所掌握的大致分三種,第一種:threadpool(線程池)+countdownlatch(程序計數(shù)器),第二種:fork/join框架,第三種jdk8并行流,下面對這幾種方式的多線程處理性能做一下比較總結(jié)。

首先,假設(shè)一種業(yè)務(wù)場景,在內(nèi)存中生成多個文件對象,這里暫定30000,(thread.sleep(時間))線程睡眠模擬業(yè)務(wù)處理業(yè)務(wù)邏輯,來比較這幾種方式的多線程處理性能。

1) 單線程

這種方式非常簡單,但是程序在處理的過程中非常的耗時,使用的時間會很長,因?yàn)槊總€線程都在等待當(dāng)前線程執(zhí)行完才會執(zhí)行,和多線程沒有多少關(guān)系,所以效率非常低。

首先創(chuàng)建文件對象,代碼如下:

java" id="highlighter_113611">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class fileinfo {
 private string filename;//文件名
 private string filetype;//文件類型
 private string filesize;//文件大小
 private string filemd5;//md5碼
 private string fileversionno;//文件版本號
 public fileinfo() {
  super();
 }
 public fileinfo(string filename, string filetype, string filesize, string filemd5, string fileversionno) {
  super();
  this.filename = filename;
  this.filetype = filetype;
  this.filesize = filesize;
  this.filemd5 = filemd5;
  this.fileversionno = fileversionno;
 }
 public string getfilename() {
  return filename;
 }
 public void setfilename(string filename) {
  this.filename = filename;
 }
 public string getfiletype() {
  return filetype;
 }
 public void setfiletype(string filetype) {
  this.filetype = filetype;
 }
 public string getfilesize() {
  return filesize;
 }
 public void setfilesize(string filesize) {
  this.filesize = filesize;
 }
 public string getfilemd5() {
  return filemd5;
 }
 public void setfilemd5(string filemd5) {
  this.filemd5 = filemd5;
 }
 public string getfileversionno() {
  return fileversionno;
 }
 public void setfileversionno(string fileversionno) {
  this.fileversionno = fileversionno;
 }

接著,模擬業(yè)務(wù)處理,創(chuàng)建30000個文件對象,線程睡眠1ms,之前設(shè)置的1000ms,發(fā)現(xiàn)時間很長,整個eclipse卡掉了,所以將時間改為了1ms。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class test {
   private static list<fileinfo> filelist= new arraylist<fileinfo>();
   public static void main(string[] args) throws interruptedexception {
     createfileinfo();
     long starttime=system.currenttimemillis();
     for(fileinfo fi:filelist){
       thread.sleep(1);
     }
     long endtime=system.currenttimemillis();
     system.out.println("單線程耗時:"+(endtime-starttime)+"ms");
   }
   private static void createfileinfo(){
     for(int i=0;i<30000;i++){
       filelist.add(new fileinfo("身份證正面照","jpg","101522","md5"+i,"1"));
     }
   }
}

測試結(jié)果如下:

Java多線程優(yōu)化方法及使用方式

可以看到,生成30000個文件對象消耗的時間比較長,接近1分鐘,效率比較低。

2) threadpool (線程池) +countdownlatch (程序計數(shù)器)

顧名思義,countdownlatch為線程計數(shù)器,他的執(zhí)行過程如下:首先,在主線程中調(diào)用await()方法,主線程阻塞,然后,將程序計數(shù)器作為參數(shù)傳遞給線程對象,最后,每個線程執(zhí)行完任務(wù)后,調(diào)用countdown()方法表示完成任務(wù)。countdown()被執(zhí)行多次后,主線程的await()會失效。實(shí)現(xiàn)過程如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class test2 {
 private static executorservice executor=executors.newfixedthreadpool(100);
 private static countdownlatch countdownlatch=new countdownlatch(100);
 private static list<fileinfo> filelist= new arraylist<fileinfo>();
 private static list<list<fileinfo>> list=new arraylist<>();
 public static void main(string[] args) throws interruptedexception {
  createfileinfo();
  addlist();
  long starttime=system.currenttimemillis();
  int i=0;
  for(list<fileinfo> fi:list){
   executor.submit(new filerunnable(countdownlatch,fi,i));
   i++;
  }
  countdownlatch.await();
  long endtime=system.currenttimemillis();
  executor.shutdown();
  system.out.println(i+"個線程耗時:"+(endtime-starttime)+"ms");
 }
 private static void createfileinfo(){
  for(int i=0;i<30000;i++){
   filelist.add(new fileinfo("身份證正面照","jpg","101522","md5"+i,"1"));
  }
 }
 private static void addlist(){
  for(int i=0;i<100;i++){
   list.add(filelist);
  }
 }
}

filerunnable類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * 多線程處理
 * @author wangsj
 *
 * @param <t>
 */
public class filerunnable<t> implements runnable {
   private countdownlatch countdownlatch;
   private list<t> list;
   private int i;
   public filerunnable(countdownlatch countdownlatch, list<t> list, int i) {
     super();
     this.countdownlatch = countdownlatch;
     this.list = list;
     this.i = i;
   }
   @override
   public void run() {
     for(t t:list){
       try {
          thread.sleep(1);
       } catch (interruptedexception e) {
          e.printstacktrace();
       }
       countdownlatch.countdown();
     }
   }
}

測試結(jié)果如下:

Java多線程優(yōu)化方法及使用方式

3) fork/join 框架

jdk從版本7開始,出現(xiàn)了fork/join框架,從字面來理解,fork就是拆分,join就是合并,所以,該框架的思想就是。通過fork拆分任務(wù),然后join來合并拆分后各個人物執(zhí)行完畢后的結(jié)果并匯總。比如,我們要計算連續(xù)相加的幾個數(shù),2+4+5+7=?,我們利用fork/join框架來怎么完成呢,思想就是拆分子任務(wù),我們可以把這個運(yùn)算拆分為兩個子任務(wù),一個計算2+4,另一個計算5+7,這是fork的過程,計算完成后,把這兩個子任務(wù)計算的結(jié)果匯總,得到總和,這是join的過程。

fork/join框架執(zhí)行思想:首先,分割任務(wù),使用fork類將大任務(wù)分割為若干子任務(wù),這個分割過程需要按照實(shí)際情況來定,直到分割出的任務(wù)足夠小。然后,join類執(zhí)行任務(wù),分割的子任務(wù)在不同的隊列里,幾個線程分別從隊列里獲取任務(wù)并執(zhí)行,執(zhí)行完的結(jié)果放到一個單獨(dú)的隊列里,最后,啟動線程,隊列里拿取結(jié)果并合并結(jié)果。

使用fork/join框架要用到幾個類,關(guān)于類的使用方式可以參考jdk的api,使用該框架,首先需要繼承forkjointask類,通常,只需要繼承他的子類recursivetask或recursiveaction即可,recursivetask,用于有返回結(jié)果的場景,recursiveaction用于沒有返回結(jié)果的場景。forkjointask的執(zhí)行需要用到forkjoinpool來執(zhí)行,該類用于維護(hù)分割出的子任務(wù)添加到不同的任務(wù)隊列。

下面是實(shí)現(xiàn)代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class test3 {
 private static list<fileinfo> filelist= new arraylist<fileinfo>();
// private static forkjoinpool forkjoinpool=new forkjoinpool(100);
// private static job<fileinfo> job=new job<>(filelist.size()/100, filelist);
 public static void main(string[] args) {
  createfileinfo();
  long starttime=system.currenttimemillis();
  forkjoinpool forkjoinpool=new forkjoinpool(100);
  //分割任務(wù)
  job<fileinfo> job=new job<>(filelist.size()/100, filelist);
  //提交任務(wù)返回結(jié)果
forkjointask<integer> fjtresult=forkjoinpool.submit(job);
//阻塞
  while(!job.isdone()){
   system.out.println("任務(wù)完成!");
  }
  long endtime=system.currenttimemillis();
  system.out.println("fork/join框架耗時:"+(endtime-starttime)+"ms");
 }
 private static void createfileinfo(){
  for(int i=0;i<30000;i++){
   filelist.add(new fileinfo("身份證正面照","jpg","101522","md5"+i,"1"));
  }
 }
}
/**
 * 執(zhí)行任務(wù)類
 * @author wangsj
 *
 */
public class job<t> extends recursivetask<integer> {
 private static final long serialversionuid = 1l;
 private int count;
 private list<t> joblist;
 public job(int count, list<t> joblist) {
  super();
  this.count = count;
  this.joblist = joblist;
 }
 /**
  * 執(zhí)行任務(wù),類似于實(shí)現(xiàn)runnable接口的run方法
  */
 @override
 protected integer compute() {
  //拆分任務(wù)
  if(joblist.size()<=count){
   executejob();
   return joblist.size();
  }else{
   //繼續(xù)創(chuàng)建任務(wù),直到能夠分解執(zhí)行
   list<recursivetask<long>> fork = new linkedlist<recursivetask<long>>();
   //拆分子任務(wù),這里采用二分法
   int countjob=joblist.size()/2;
   list<t> leftlist=joblist.sublist(0, countjob);
   list<t> rightlist=joblist.sublist(countjob, joblist.size());
   //分配任務(wù)
   job leftjob=new job<>(count,leftlist);
   job rightjob=new job<>(count,rightlist);
   //執(zhí)行任務(wù)
   leftjob.fork();
   rightjob.fork();
   return integer.parseint(leftjob.join().tostring())
     +integer.parseint(rightjob.join().tostring());
  }
 }
 /**
  * 執(zhí)行任務(wù)方法
  */
 private void executejob() {
  for(t job:joblist){
   try {
    thread.sleep(1);
   } catch (interruptedexception e) {
    e.printstacktrace();
   }
  }
 }

測試結(jié)果如下:

Java多線程優(yōu)化方法及使用方式

4) jdk8 并行流

并行流是jdk8的新特性之一,思想就是將一個順序執(zhí)行的流變?yōu)橐粋€并發(fā)的流,通過調(diào)用parallel()方法來實(shí)現(xiàn)。并行流將一個流分成多個數(shù)據(jù)塊,用不同的線程來處理不同的數(shù)據(jù)塊的流,最后合并每個塊數(shù)據(jù)流的處理結(jié)果,類似于fork/join框架。

并行流默認(rèn)使用的是公共線程池forkjoinpool,他的線程數(shù)是使用的默認(rèn)值,根據(jù)機(jī)器的核數(shù),我們可以適當(dāng)調(diào)整線程數(shù)的大小。線程數(shù)的調(diào)整通過以下方式來實(shí)現(xiàn)。

?
1
system.setproperty("java.util.concurrent.forkjoinpool.common.parallelism", "100");

以下是代碼的實(shí)現(xiàn)過程,非常簡單:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class test4 {
private static list<fileinfo> filelist= new arraylist<fileinfo>();
public static void main(string[] args) {
//    system.setproperty("java.util.concurrent.forkjoinpool.common.parallelism", "100");
   createfileinfo();
   long starttime=system.currenttimemillis();
   filelist.parallelstream().foreach(e ->{
     try {
        thread.sleep(1);
     } catch (interruptedexception f) {
        f.printstacktrace();
     }
   });
   long endtime=system.currenttimemillis();
   system.out.println("jdk8并行流耗時:"+(endtime-starttime)+"ms");
}
private static void createfileinfo(){
   for(int i=0;i<30000;i++){
     filelist.add(new fileinfo("身份證正面照","jpg","101522","md5"+i,"1"));
   }
}
}

下面是測試,第一次沒有設(shè)置線程池的數(shù)量,采用默認(rèn),測試結(jié)果如下:

Java多線程優(yōu)化方法及使用方式

我們看到,結(jié)果并不是很理想,耗時較長,接下來設(shè)置線程池的數(shù)量大小,即添加如下代碼:

?
1
system.setproperty("java.util.concurrent.forkjoinpool.common.parallelism", "100");

接著進(jìn)行測試,結(jié)果如下:

Java多線程優(yōu)化方法及使用方式

這次耗時較小,比較理想。

三、總結(jié)

綜上幾種情況來看,以單線程作為參考,耗時最長的還是原生的fork/join框架,這里邊盡管配置了線程池的數(shù)量,但效果較精確配置了線程池數(shù)量的jdk8并行流較差。并行流實(shí)現(xiàn)代碼簡單易懂,不需要我們寫多余的for循環(huán),一個parallelstream方法全部搞定,代碼量大大的減少了,其實(shí),并行流的底層還是使用的fork/join框架,這就要求我們在開發(fā)的過程中靈活使用各種技術(shù),分清各種技術(shù)的優(yōu)缺點(diǎn),從而能夠更好的為我們服務(wù)。

原文鏈接:http://www.cnblogs.com/10158wsj/p/8338367.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久久噜噜噜久久熟有声小说 | 亚洲最新无码中文字幕久久 | 性欧美xxxx精品xxxxrb | 欧美在线观看视频一区二区 | 一级国产航空美女毛片内谢 | 55夜色66夜色国产精品视频 | 国产午夜精品久久久久婷 | 91精品国产乱码久久桃 | 日韩视频网址 | 久久精品中文字幕 | 素人视频免费观看 | 爱福利视频网 | 久久99深爱久久99精品 | 一级黄色毛片免费 | 本色视频aaaaaa一级网站 | 国产欧美精品一区二区三区四区 | 毛片大全免费 | 九九热精品免费 | 欧美a级大胆视频 | 欧美日韩色 | 欧美综合日韩 | 成人毛片视频免费看 | 凹凸成人精品亚洲精品密奴 | 泰剧19禁啪啪无遮挡大尺度 | 成人免费电影在线观看 | 羞羞视频免费网站含羞草 | 91久久久国产精品 | 欧美a在线观看 | 欧美www| 涩涩屋av| 成年人网站视频免费 | 正在播放91 | 狠狠干夜夜草 | 综合精品| 黄色毛片一级视频 | 久久99精品久久久久久久久久久久 | 日韩视频在线一区二区三区 | 婷婷亚洲一区二区三区 | 久久伊人精品视频 | 91一区二区三区久久久久国产乱 | 红杏成人性视频免费看 |