欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

多線程如何解決for循環(huán)效率的問(wèn)題

 更新時(shí)間:2021年06月17日 11:42:44   作者:格子間里格子衫  
這篇文章主要介紹了多線程如何解決for循環(huán)效率的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

多線程解決for循環(huán)效率問(wèn)題

在for里面,如果執(zhí)行一次for里面的內(nèi)容所需時(shí)間比較長(zhǎng),可以使用線程池來(lái)提高for循環(huán)的效率

public class TreadFor {
private static final int loopNum = 1*10;  
    public static void main(String args[]) throws InterruptedException {  
    	TreadFor TestThreadPool = new TreadFor();  
        long bt = System.currentTimeMillis();  
        List<String> list = new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        list.add("6");
        list.add("7");
        list.add("8");
        list.add("9");
        TestThreadPool.m1(list);  
        long et2 = System.currentTimeMillis();  
        System.out.println("[1]耗時(shí):"+(et2 - bt)+ "ms");  
        Thread thread = new Thread();  
        long at = System.currentTimeMillis();  
        TestThreadPool.m2();
        long et3 = System.currentTimeMillis();
        System.out.println("[2]耗時(shí):"+(et3 - at)+ "ms");
    }  
  
    public void m1( List<String> list) {
        ExecutorService pool = Executors.newCachedThreadPool();  
        for (int i = 0; i < list.size(); i++) {
        	String str = list.get(i);
        	System.out.println(list.get(i));
            Runnable run = new Runnable() {  
                public void run() {  
                    try {  
                        new Thread().sleep(1000);
                        //模擬耗時(shí)操作
                    	System.out.println("[1]" + Thread.currentThread().getName()+"----"+str);
                    } catch (Exception e) {  
                    }  
                }  
            }; 
            pool.execute(run);  
		
		}
        System.out.println("[1] done!");
        pool.shutdown();  
    }  
  
    public void m2() { 
    	AtomicInteger connectionIds = new AtomicInteger(0);
        for (int index = 0; index < loopNum; index++) {  
            try {  
                new Thread().sleep(1000);  //模擬耗時(shí)操作
                System.out.println("[2]" + Thread.currentThread().getName());
                
            } catch (Exception e) {  
                e.printStackTrace();  
            } 
        }  
        System.out.println("[2] done!");
    }  
}

其中遍歷list,給方法傳參,參數(shù)最終也可以進(jìn)入的線程里;

運(yùn)行結(jié)果:

由打印結(jié)果可知:m1方法是用到了多線程的,多線程此時(shí)被線程池管理;而m2方法始終是main主線程執(zhí)行的。

采用先把要執(zhí)行的“耗時(shí)”內(nèi)容放到一個(gè)線程的執(zhí)行主體(run方法)里面,再用線程池執(zhí)行該線程,可大大減少for循環(huán)的耗時(shí)。

但這種情況不適合for次數(shù)較大的情形,因?yàn)槊垦h(huán)一次,就開(kāi)辟一個(gè)線程,開(kāi)銷較大。

注意這種不叫高并發(fā),只是相當(dāng)于原來(lái)由一個(gè)工人干的活現(xiàn)在由多個(gè)工人協(xié)作完成一樣。

Java 多個(gè)線程交替循環(huán)執(zhí)行

有些時(shí)候面試官經(jīng)常會(huì)問(wèn),兩個(gè)線程怎么交替執(zhí)行呀,如果是三個(gè)線程,又怎么交替執(zhí)行呀,這種問(wèn)題一般人還真不一定能回答上來(lái)。多線程這塊如果理解的不好,學(xué)起來(lái)是很吃力的,更別說(shuō)面試了。

下面我們就來(lái)剖析一下怎么實(shí)現(xiàn)多個(gè)線程順序輸出。

兩個(gè)線程循環(huán)交替打印

//首先我們來(lái)看一種比較簡(jiǎn)單的方式
public class ThreadCq {
 public static void main(String[] args) {
   Stack<Integer> stack = new Stack<>();
   for(int i=1;i<100;i++) {
    stack.add(i);
   }
   Draw draw = new Draw(stack);
   new Thread(draw).start();
   new Thread(draw).start();
 }
}
 
class Draw implements Runnable{
 private Stack<Integer> stack;
 public Draw(Stack<Integer> stack) {
  this.stack = stack;
 }
 
 @Override
 public void run() {
  while(!stack.isEmpty()) {
   synchronized (this) {
    notify();
    System.out.println(Thread.currentThread().getName()+"---"+stack.pop());
    try {
     wait();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
 }
}

這種方式是用Condition對(duì)象來(lái)完成的:

public class ThreadCq3 {
 //聲明一個(gè)鎖
 static ReentrantLock lock = new ReentrantLock();
 public static void main(String[] args) {
  //創(chuàng)建兩個(gè)Condition對(duì)象
  Condition c1 = lock.newCondition();
  Condition c2 = lock.newCondition();
  Stack<Integer> stack = new Stack<>();
  for (int i = 0; i <= 100; i++) {
   stack.add(i);
  }
 
  new Thread(() -> {
   try {
    Thread.sleep(500);
   } catch (InterruptedException e1) {
    e1.printStackTrace();
   }
   while (true) {
    lock.lock();
    // 打印偶數(shù)
    try {
     if (stack.peek() % 2 != 0) {
      c1.await();
     }
     System.out.println(Thread.currentThread().getName() + "-----" + stack.pop());
     c2.signal();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     lock.unlock();
    }
   }
  }).start();
  
  new Thread(() -> {
   while (true) {
    try {
     Thread.sleep(500);
    } catch (InterruptedException e1) {
     e1.printStackTrace();
    }
    lock.lock();
    try {
     // 打印奇數(shù)
     if (stack.peek() % 2 != 1) {
      c2.await();
     }
     System.out.println(Thread.currentThread().getName() + "-----" + stack.pop());
     c1.signal();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     lock.unlock();
    }
   }
  }).start();
 }
}

這種方式是通過(guò)Semaphore來(lái)實(shí)現(xiàn)的:

public class ThreadCq4 {
 //利用信號(hào)量來(lái)限制
 private static Semaphore s1 = new Semaphore(1);
 private static Semaphore s2 = new Semaphore(1);
 public static void main(String[] args) {
  
  try {
   //首先調(diào)用s2為 acquire狀態(tài)
   s1.acquire();
//   s2.acquire();  調(diào)用s1或者s2先占有一個(gè)
  } catch (InterruptedException e1) {
   e1.printStackTrace();
  }
  
  new Thread(()->{
   while(true) {
    try {
     s1.acquire();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    try {
     Thread.sleep(500);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("A");
    s2.release();
   }
  }).start();
  
  new Thread(()->{
   while(true) {
    try {
     s2.acquire();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    try {
     Thread.sleep(500);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("B");
    s1.release();
   }
  }).start();
 }
}

上面就是三種比較常用的,最常用的要屬第一種和第二種。

三個(gè)線程交替打印輸出

上面我們看了兩個(gè)線程依次輸出的實(shí)例,這里我們來(lái)看看三個(gè)線程如何做呢。

public class LockCond {
 private static int count = 0;
 private static Lock lock = new ReentrantLock();
 public static void main(String[] args) {
  Condition c1 = lock.newCondition();
  Condition c2 = lock.newCondition();
  Condition c3 = lock.newCondition();
  new Thread(()->{
   while(true) {
    lock.lock();
    try {
     while(count %3 != 0) {
      //剛開(kāi)始count為0  0%3=0 所以此線程執(zhí)行  執(zhí)行完之后 喚醒現(xiàn)成2,由于此時(shí)count已經(jīng)進(jìn)行了++,所有while成立,c1進(jìn)入等待狀態(tài),其他兩個(gè)也一樣
      c1.await();
     }
     System.out.println(Thread.currentThread().getName()+"========:A");
     count++;
     //喚醒線程2
     c2.signal(); 
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     lock.unlock();
    }
   }
  }) .start();
  
  new Thread(()->{
   while(true) {
    lock.lock();
    try {
     while(count %3 != 1) {
      c2.await();
     }
     System.out.println(Thread.currentThread().getName()+"========:B");
     count++;
     //喚醒線程3
     c3.signal();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     lock.unlock();
    }
   }
  }) .start();
  
  new Thread(()->{
   while(true) {
    lock.lock();
    try {
     while(count %3 != 2) {
      c3.await();
     }
     System.out.println(Thread.currentThread().getName()+"========:C");
     count++;
     //喚醒線程1
     c1.signal();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } finally {
     lock.unlock();
    }
   }
  }) .start();
 }
}

三個(gè)線程的也可以寫(xiě)三種,這里寫(xiě)一種就行了,寫(xiě)法和上面兩個(gè)線程的都一樣。大家可以自己試一下。

Condition介紹

我們?cè)跊](méi)有學(xué)習(xí)Lock之前,使用的最多的同步方式應(yīng)該是synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)同步方式了。配合Object的wait()、notify()系列方法可以實(shí)現(xiàn)等待/通知模式。Condition接口也提供了類似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的。Object和Condition接口的一些對(duì)比。摘自《Java并發(fā)編程的藝術(shù)》

Condition接口常用方法

condition可以通俗的理解為條件隊(duì)列。當(dāng)一個(gè)線程在調(diào)用了await方法以后,直到線程等待的某個(gè)條件為真的時(shí)候才會(huì)被喚醒。這種方式為線程提供了更加簡(jiǎn)單的等待/通知模式。Condition必須要配合鎖一起使用,因?yàn)閷?duì)共享狀態(tài)變量的訪問(wèn)發(fā)生在多線程環(huán)境下。一個(gè)Condition的實(shí)例必須與一個(gè)Lock綁定,因此Condition一般都是作為L(zhǎng)ock的內(nèi)部實(shí)現(xiàn)。

await() :造成當(dāng)前線程在接到信號(hào)或被中斷之前一直處于等待狀態(tài)。

await(long time, TimeUnit unit) :造成當(dāng)前線程在接到信號(hào)、被中斷或到達(dá)指定等待時(shí)間之前一直處于等待狀態(tài)。

awaitNanos(long nanosTimeout) :造成當(dāng)前線程在接到信號(hào)、被中斷或到達(dá)指定等待時(shí)間之前一直處于等待狀態(tài)。返回值表示剩余時(shí)間,如果在nanosTimesout之前喚醒,那么返回值 = nanosTimeout - 消耗時(shí)間,如果返回值 <= 0 ,則可以認(rèn)定它已經(jīng)超時(shí)了。

awaitUninterruptibly() :造成當(dāng)前線程在接到信號(hào)之前一直處于等待狀態(tài)?!咀⒁猓涸摲椒▽?duì)中斷不敏感】。

awaitUntil(Date deadline) :造成當(dāng)前線程在接到信號(hào)、被中斷或到達(dá)指定最后期限之前一直處于等待狀態(tài)。如果沒(méi)有到指定時(shí)間就被通知,則返回true,否則表示到了指定時(shí)間,返回返回false。

signal() :?jiǎn)拘岩粋€(gè)等待線程。該線程從等待方法返回前必須獲得與Condition相關(guān)的鎖。

signal()All :?jiǎn)拘阉械却€程。能夠從等待方法返回的線程必須獲得與Condition相關(guān)的鎖。

Semaphore介紹

Semaphore 是 synchronized 的加強(qiáng)版,作用是控制線程的并發(fā)數(shù)量。就這一點(diǎn)而言,單純的synchronized 關(guān)鍵字是實(shí)現(xiàn)不了的。他可以保證某一個(gè)資源在一段區(qū)間內(nèi)有多少給線程可以去訪問(wèn)。

從源碼我們可以看出來(lái),它new了一個(gè)靜態(tài)內(nèi)部類,繼承Sync接口。他同時(shí)也提供了一些構(gòu)造方法

比如說(shuō)通過(guò)這個(gè)構(gòu)造方法可以創(chuàng)建一個(gè)是否公平的Semaphore類。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 很詳細(xì)的Log4j配置步驟

    很詳細(xì)的Log4j配置步驟

    Log4J的配置文件(Configuration File)就是用來(lái)設(shè)置記錄器的級(jí)別、存放器和布局的,它可接key=value格式的設(shè)置或xml格式的設(shè)置信息。通過(guò)配置,可以創(chuàng)建出Log4J的運(yùn)行環(huán)境。
    2008-11-11
  • 基于SpringBoot實(shí)現(xiàn)大文件分塊上傳功能

    基于SpringBoot實(shí)現(xiàn)大文件分塊上傳功能

    這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)大文件分塊上傳功能,實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單,核心就是客戶端把大文件按照一定規(guī)則進(jìn)行拆分,比如20MB為一個(gè)小塊,分解成一個(gè)一個(gè)的文件塊,然后把這些文件塊單獨(dú)上傳到服務(wù)端,需要的朋友可以參考下
    2024-09-09
  • springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式

    springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式

    這篇文章主要介紹了springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 使用純Java實(shí)現(xiàn)一個(gè)WebSSH項(xiàng)目的示例代碼

    使用純Java實(shí)現(xiàn)一個(gè)WebSSH項(xiàng)目的示例代碼

    這篇文章主要介紹了使用純Java實(shí)現(xiàn)一個(gè)WebSSH項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • SpringMVC日期類型參數(shù)傳遞實(shí)現(xiàn)步驟講解

    SpringMVC日期類型參數(shù)傳遞實(shí)現(xiàn)步驟講解

    這篇文章主要介紹了SpringMVC日期類型參數(shù)傳遞實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • Java用單向環(huán)形鏈表來(lái)解決約瑟夫環(huán)Josepfu問(wèn)題

    Java用單向環(huán)形鏈表來(lái)解決約瑟夫環(huán)Josepfu問(wèn)題

    如果把單鏈表的最后一個(gè)節(jié)點(diǎn)的指針指向鏈表頭部,而不是指向NULL,那么就構(gòu)成了一個(gè)單向循環(huán)鏈表,通俗講就是把尾節(jié)點(diǎn)的下一跳指向頭結(jié)點(diǎn)
    2021-10-10
  • springBoot整合Eureka啟動(dòng)失敗的解決方案

    springBoot整合Eureka啟動(dòng)失敗的解決方案

    這篇文章主要介紹了springBoot整合Eureka啟動(dòng)失敗的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java編程獲取當(dāng)前屏幕分辨率的方法示例

    Java編程獲取當(dāng)前屏幕分辨率的方法示例

    這篇文章主要介紹了Java編程獲取當(dāng)前屏幕分辨率的方法,涉及java針對(duì)系統(tǒng)硬件信息的相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08
  • IDEA安裝后找不到.vmoptions文件的問(wèn)題及解決

    IDEA安裝后找不到.vmoptions文件的問(wèn)題及解決

    這篇文章主要介紹了IDEA安裝后找不到.vmoptions文件的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • MyBatis?核心組件Configuration實(shí)例詳解

    MyBatis?核心組件Configuration實(shí)例詳解

    Configuration用于描述 MyBatis 的主配置信息,其他組件需要獲取配置信息時(shí),直接通過(guò) Configuration 對(duì)象獲取,這篇文章主要介紹了MyBatis核心組件Configuration,需要的朋友可以參考下
    2023-08-08

最新評(píng)論