溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

多線程for循環(huán)效率的問題怎么解決

發(fā)布時間:2021-06-17 14:45:53 來源:億速云 閱讀:254 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“多線程for循環(huán)效率的問題怎么解決”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

在for里面,如果執(zhí)行一次for里面的內(nè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]耗時:"+(et2 - bt)+ "ms");  
        Thread thread = new Thread();  
        long at = System.currentTimeMillis();  
        TestThreadPool.m2();
        long et3 = System.currentTimeMillis();
        System.out.println("[2]耗時:"+(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);
                        //模擬耗時操作
                    	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);  //模擬耗時操作
                System.out.println("[2]" + Thread.currentThread().getName());
                
            } catch (Exception e) {  
                e.printStackTrace();  
            } 
        }  
        System.out.println("[2] done!");
    }  
}

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

運行結(jié)果:

多線程for循環(huán)效率的問題怎么解決

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

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

但這種情況不適合for次數(shù)較大的情形,因為每循環(huán)一次,就開辟一個線程,開銷較大。

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

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

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

下面我們就來剖析一下怎么實現(xiàn)多個線程順序輸出。

兩個線程循環(huá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對象來完成的:

public class ThreadCq3 {
 //聲明一個鎖
 static ReentrantLock lock = new ReentrantLock();
 public static void main(String[] args) {
  //創(chuàng)建兩個Condition對象
  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();
 }
}

這種方式是通過Semaphore來實現(xiàn)的:

public class ThreadCq4 {
 //利用信號量來限制
 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先占有一個
  } 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();
 }
}

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

三個線程交替打印輸出

上面我們看了兩個線程依次輸出的實例,這里我們來看看三個線程如何做呢。

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) {
      //剛開始count為0  0%3=0 所以此線程執(zhí)行  執(zhí)行完之后 喚醒現(xiàn)成2,由于此時count已經(jīng)進(jìn)行了++,所有while成立,c1進(jìn)入等待狀態(tài),其他兩個也一樣
      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();
 }
}

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

Condition介紹

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

多線程for循環(huán)效率的問題怎么解決

Condition接口常用方法

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

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

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

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

awaitUninterruptibly() :造成當(dāng)前線程在接到信號之前一直處于等待狀態(tài)?!咀⒁猓涸摲椒▽χ袛嗖幻舾小?。

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

signal() :喚醒一個等待線程。該線程從等待方法返回前必須獲得與Condition相關(guān)的鎖。

signal()All :喚醒所有等待線程。能夠從等待方法返回的線程必須獲得與Condition相關(guān)的鎖。

Semaphore介紹

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

多線程for循環(huán)效率的問題怎么解決

多線程for循環(huán)效率的問題怎么解決

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

多線程for循環(huán)效率的問題怎么解決

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

“多線程for循環(huán)效率的問題怎么解決”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI