中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

如何在Java中實現線程同步

發布時間:2021-05-18 17:47:52 來源:億速云 閱讀:116 作者:Leah 欄目:編程語言

如何在Java中實現線程同步?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

1. Semaphore

1.1 二進制Semaphore

Semaphore算是比較高級點的線程同步工具了,在許多其他語言里也有類似的實現。Semaphore有一個最大的好處就是在初始化時,可以顯式的控制并發數。其內部維護這一個c計數器,當計數器小于等于0時,是不允許其他線程訪問并發區域的,反之則可以,因此,若將并發數設置為1,則可以確保單一線程同步。下面的例子模擬多線程打印,每個線程提交打印申請,然后執行打印,最后宣布打印結束,代碼如下:

import java.util.concurrent.Semaphore;
public class Program{
    public static void main(String[] agrs){
        PrintQueue p=new PrintQueue();
        Thread[] ths=new Thread[10];
        for(int i=0;i<10;i++){
            ths[i]=new Thread(new Job(p),"Thread"+i);
        }
        for(int i=0;i<10;i++){
            ths[i].start();
        }
    }
}
class PrintQueue{
    private Semaphore s;
    public PrintQueue(){
        s=new Semaphore(1);//二進制信號量
    }
    public void printJob(Object document){
        try{
            s.acquire();
            long duration=(long)(Math.random()*100);
            System.out.printf("線程名:%s 睡眠:%d",Thread.currentThread().getName(),duration);
            Thread.sleep(duration);
        }
        catch(InterruptedException e){
            e.printStackTrace();
        }
        finally{
            s.release();
        }
    }
}
class Job implements Runnable{
    private PrintQueue p;
    public Job(PrintQueue p){
        this.p=p;
    }
    @Override
    public void run(){
        System.out.printf("%s:正在打印一個任務\n ",Thread.currentThread().getName());
        this.p.printJob(new Object());
        System.out.printf("%s:文件已打印完畢\n ",Thread.currentThread().getName());
    }
}

執行結果如下:

 Thread0:正在打印一個任務
 Thread9:正在打印一個任務
 Thread8:正在打印一個任務
 Thread7:正在打印一個任務
 Thread6:正在打印一個任務
 Thread5:正在打印一個任務
 Thread4:正在打印一個任務
 Thread3:正在打印一個任務
 Thread2:正在打印一個任務
 Thread1:正在打印一個任務
 線程名:Thread0 睡眠:32  Thread0:文件已打印完畢
 線程名:Thread9 睡眠:44  Thread9:文件已打印完畢
 線程名:Thread8 睡眠:45  Thread8:文件已打印完畢
 線程名:Thread7 睡眠:65  Thread7:文件已打印完畢
 線程名:Thread6 睡眠:12  Thread6:文件已打印完畢
 線程名:Thread5 睡眠:72  Thread5:文件已打印完畢
 線程名:Thread4 睡眠:98  Thread4:文件已打印完畢
 線程名:Thread3 睡眠:58  Thread3:文件已打印完畢
 線程名:Thread2 睡眠:24  Thread2:文件已打印完畢
 線程名:Thread1 睡眠:93  Thread1:文件已打印完畢

可以看到,所有線程提交打印申請后,按照并發順序一次執行,沒有任何并發沖突,誰先獲得信號量,誰就先執行,其他剩余線程均等待。這里面還有一個公平信號與非公平信號之說:基本上java所有的多線程工具都支持初始化的時候指定一個布爾變量,true時表明公平,即所有處于等待的線程被篩選的條件為“誰等的時間長就選誰進行執行”,有點first in first out的感覺,而false時則表明不公平(默認是不non-fairness),即所有處于等待的線程被篩選執行是隨機的。這也就是為什么多線程往往執行順序比較混亂的原因。

1.2 多重并發控制

若將上面的代碼改為s=new Semaphore(3);//即讓其每次可以并發3條線程,則輸出如下:

Thread0:正在打印一個任務
 Thread9:正在打印一個任務
 Thread8:正在打印一個任務
 Thread7:正在打印一個任務
 Thread6:正在打印一個任務
 Thread5:正在打印一個任務
 Thread3:正在打印一個任務
 Thread4:正在打印一個任務
 Thread2:正在打印一個任務
 Thread1:正在打印一個任務
 線程名:Thread9 睡眠:26線程名:Thread8 睡眠:46線程名:Thread0 睡眠:79  Thread9:文件已打印完畢
 線程名:Thread7 睡眠:35  Thread8:文件已打印完畢
 線程名:Thread6 睡眠:90  Thread7:文件已打印完畢
 線程名:Thread5 睡眠:40  Thread0:文件已打印完畢
 線程名:Thread3 睡眠:84  Thread5:文件已打印完畢
 線程名:Thread4 睡眠:13  Thread4:文件已打印完畢
 線程名:Thread2 睡眠:77  Thread6:文件已打印完畢
 線程名:Thread1 睡眠:12  Thread1:文件已打印完畢
   Thread3:文件已打印完畢
   Thread2:文件已打印完畢

很明顯已經并發沖突了。若要實現分組(每組3個)并發嗎,則每一組也要進行同步,代碼修改如下:

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Program{
    public static void main(String[] agrs){
        PrintQueue p=new PrintQueue();
        Thread[] ths=new Thread[10];
        for(int i=0;i<10;i++){
            ths[i]=new Thread(new Job(p),"Thread"+i);
        }
        for(int i=0;i<10;i++){
            ths[i].start();
        }
    }
}
class PrintQueue{
    private Semaphore s;
    private boolean[] freePrinters;
    private Lock lock;
    public PrintQueue(){
        s=new Semaphore(3);//二進制信號量
        freePrinters=new boolean[3];
        for(int i=0;i<3;i++){
            freePrinters[i]=true;
        }
        lock=new ReentrantLock();
    }
    public void printJob(Object document){
        try{
            s.acquire();
            int printerIndex=getIndex();
                long duration=(long)(Math.random()*100);
                System.out.printf("線程名:%s 睡眠:%d\n",Thread.currentThread().getName(),duration);
                Thread.sleep(duration);
                freePrinters[printerIndex]=true;//恢復信號,供下次使用
        }
        catch(InterruptedException e){
            e.printStackTrace();
        }
        finally{
            s.release();
        }
    }
    //返回一個內部分組后的同步索引
    public int getIndex(){
        int index=-1;
        try{
            lock.lock();
            for(int i=0;i<freePrinters.length;i++){
                if(freePrinters[i]){
                    freePrinters[i]=false;
                    index=i;
                    break;
                }
            }
     }
     catch(Exception e){
         e.printStackTrace();
     }
     finally{
         lock.unlock();
     }
     return index;
    }
}
class Job implements Runnable{
    private PrintQueue p;
    public Job(PrintQueue p){
        this.p=p;
    }
    @Override
    public void run(){
        System.out.printf("%s:正在打印一個任務\n ",Thread.currentThread().getName());
        this.p.printJob(new Object());
        System.out.printf(" %s:文件已打印完畢\n ",Thread.currentThread().getName());
    }
}

其中getIndex()方法主要為了維護內部分組后(支持并發3個)組內數據的同步(用lock來同步)。

輸出如下:

 Thread0:正在打印一個任務
 Thread9:正在打印一個任務
 Thread8:正在打印一個任務
 Thread7:正在打印一個任務
 Thread6:正在打印一個任務
 Thread5:正在打印一個任務
 Thread4:正在打印一個任務
 Thread3:正在打印一個任務
 Thread2:正在打印一個任務
 Thread1:正在打印一個任務
 線程名:Thread0 睡眠:82  打印機:0號
線程名:Thread8 睡眠:61  打印機:2號
線程名:Thread9 睡眠:19  打印機:1號
  Thread9:文件已打印完畢
 線程名:Thread7 睡眠:82  打印機:1號
  Thread8:文件已打印完畢
 線程名:Thread6 睡眠:26  打印機:2號
  Thread0:文件已打印完畢
 線程名:Thread5 睡眠:31  打印機:0號
  Thread6:文件已打印完畢
 線程名:Thread4 睡眠:44  打印機:2號
  Thread7:文件已打印完畢
 線程名:Thread3 睡眠:54  打印機:1號
  Thread5:文件已打印完畢
 線程名:Thread2 睡眠:48  打印機:0號
  Thread4:文件已打印完畢
 線程名:Thread1 睡眠:34  打印機:2號
  Thread3:文件已打印完畢
   Thread2:文件已打印完畢
   Thread1:文件已打印完畢

2. CountDownLatch

CountDownLatch同樣也是支持多任務并發的一個工具。它主要用于“等待多個并發事件”,它內部也有一個計數器,當調用await()方法時,線程處于等待狀態,只有當內部計數器為0時才繼續(countDown()方法來減少計數),也就說,假若有一個需求是這樣的:主線程等待所有子線程都到達某一條件時才執行,那么只需要主線程await,然后在啟動每個子線程的時候進行countDown操作。下面模擬了一個開會的例子,只有當所有人員都到齊了,會議才能開始。

import java.util.concurrent.CountDownLatch;
public class Program{
    public static void main(String[] agrs){
        //開啟可容納10人的會議室
        VideoConference v=new VideoConference(10);
        new Thread(v).start();
        //參與人員陸續進場
        for(int i=0;i<10;i++){
            Participant p=new Participant(i+"號人員",v);
            new Thread(p).start();
        }
    }
}
class VideoConference implements Runnable{
    private CountDownLatch controller;
    public VideoConference(int num){
        controller=new CountDownLatch(num);
    }
    public void arrive(String name){
        System.out.printf("%s 已經到達!\n",name);
        controller.countDown();
        System.out.printf("還需要等 %d 個成員!\n",controller.getCount());
    }
    @Override
    public void run(){
        try{
            System.out.printf("會議正在初始化...!\n");
            controller.await();
            System.out.printf("所有人都到齊了,開會吧!\n");
        }
        catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}
class Participant implements Runnable{
    private VideoConference conference;
    private String name;
    public Participant(String name,VideoConference conference){
        this.name=name;
        this.conference=conference;
    }
    @Override
    public void run(){
        long duration=(long)(Math.random()*100);
        try{
            Thread.sleep(duration);
            conference.arrive(this.name);
     }
     catch(InterruptedException e){
     }
    }
}

輸出:

會議正在初始化...!
0號人員 已經到達!
還需要等 9 個成員!
1號人員 已經到達!
還需要等 8 個成員!
9號人員 已經到達!
還需要等 7 個成員!
4號人員 已經到達!
還需要等 6 個成員!
8號人員 已經到達!
還需要等 5 個成員!
5號人員 已經到達!
還需要等 4 個成員!
6號人員 已經到達!
還需要等 3 個成員!
3號人員 已經到達!
還需要等 2 個成員!
7號人員 已經到達!
還需要等 1 個成員!
2號人員 已經到達!
還需要等 0 個成員!
所有人都到齊了,開會吧!

3. Phaser

import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.util.Date;
public class Program{
    public static void main(String[] agrs){
        Phaser phaser=new Phaser(3);
        FileSearch system=new FileSearch("C:\\Windows", "log",phaser);
        FileSearch apps=new FileSearch("C:\\Program Files","log",phaser);
        FileSearch documents=new FileSearch("C:\\Documents And Settings","log",phaser);
        Thread systemThread=new Thread(system,"System");
        systemThread.start();
        Thread appsThread=new Thread(apps,"Apps");
        appsThread.start();
        Thread documentsThread=new Thread(documents, "Documents");
        documentsThread.start();
        try {
            systemThread.join();
            appsThread.join();
            documentsThread.join();
            } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Terminated: "+ phaser.isTerminated());
    }
}
class FileSearch implements Runnable{
    private String initPath;
    private String end;
    private List<String> results;
    private Phaser phaser;
    public FileSearch(String initPath,String end,Phaser phaser){
        this.initPath=initPath;
        this.end=end;
        this.results=new ArrayList<String>();
        this.phaser=phaser;
    }
    private void directoryProcess(File file){
        File[] files=file.listFiles();
        if(files!=null){
            for(int i=0;i<files.length;i++){
                if(files[i].isDirectory()){
                    directoryProcess(files[i]);
                }
                else{
                    fileProcess(files[i]);
                }
            }
        }
    }
    private void fileProcess(File file){
        if(file.getName().endsWith(end)){
            results.add(file.getAbsolutePath());
        }
    }
    private void filterResults(){
        List<String> newResults=new ArrayList<String>();
        long actualDate=new Date().getTime();
        for(int i=0;i<results.size();i++){
            File file=new File(results.get(i));
            long fileDate=file.lastModified();
            if(actualDate-fileDate<TimeUnit.MILLISECONDS.convert(1,TimeUnit.DAYS)){
                newResults.add(results.get(i));
            }
        }
        results=newResults;
    }
    private boolean checkResults(){
        if(results.isEmpty()){
            System.out.printf("%s: Phase %d: 0 results.\n",Thread.currentThread().getName(),phaser.getPhase());
            System.out.printf("%s: Phase %d: End.\n",Thread.currentThread().getName(),phaser.getPhase());
            phaser.arriveAndDeregister();
        }
        else{
            System.out.printf("%s: Phase %d: %d results.\n",Thread.currentThread().getName(),phaser.getPhase(),results.size());
             phaser.arriveAndAwaitAdvance();
            return true;
        }
    }
    private void showInfo() {
        for (int i=0; i<results.size(); i++){
            File file=new File(results.get(i));
            System.out.printf("%s: %s\n",Thread.currentThread().getName(),file.getAbsolutePath());
        }
        phaser.arriveAndAwaitAdvance();
    }
    @Override
    public void run(){
        File file=new File(initPath);
        if(file.isDirectory()){
            directoryProcess(file);
        }
        if(!checkResults()){
            return;
        }
        filterResults();
        if(!checkResults()){
            return;
        }
        showInfo();
        phaser.arriveAndDeregister();
        System.out.printf("%s: Work completed.\n",Thread.currentThread().getName());
    }
}

運行結果:

Apps: Phase 0: 4 results.
System: Phase 0: 27 results.

Java的特點有哪些

Java的特點有哪些 1.Java語言作為靜態面向對象編程語言的代表,實現了面向對象理論,允許程序員以優雅的思維方式進行復雜的編程。 2.Java具有簡單性、面向對象、分布式、安全性、平臺獨立與可移植性、動態性等特點。 3.使用Java可以編寫桌面應用程序、Web應用程序、分布式系統和嵌入式系統應用程序等。

看完上述內容,你們掌握如何在Java中實現線程同步的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

呼图壁县| 莒南县| 太和县| 惠来县| 荣成市| 永年县| 平远县| 衡水市| 孝昌县| 六盘水市| 嘉兴市| 昌邑市| 山东| 手机| 隆化县| 丹东市| 汪清县| 滕州市| 咸宁市| 屏南县| 宁化县| 水城县| 光山县| 铁力市| 海兴县| 梁平县| 嵊州市| 嵩明县| 芦溪县| 疏勒县| 安义县| 会泽县| 逊克县| 大邑县| 石林| 宁德市| 博客| 和龙市| 陆河县| 浦北县| 隆子县|