Exchanger的工作原理及例項

carson0408發表於2018-03-07

1.實現原理        

          Exchanger(交換者)是一個用於執行緒間協作的工具類。Exchanger用於進行執行緒間的資料交換。它提供一個同步點,在這個同步點兩個執行緒可以交換彼此的資料。這兩個執行緒通過exchange方法交換資料, 如果第一個執行緒先執行exchange方法,它會一直等待第二個執行緒也執行exchange,當兩個執行緒都到達同步點時,這兩個執行緒就可以交換資料,將本執行緒生產出來的資料傳遞給對方。因此使用Exchanger的重點是成對的執行緒使用exchange()方法,當有一對執行緒達到了同步點,就會進行交換資料。因此該工具類的執行緒物件是成對的。

       Exchanger類提供了兩個方法,String exchange(V x):用於交換,啟動交換並等待另一個執行緒呼叫exchange;String exchange(V x,long timeout,TimeUnit unit):用於交換,啟動交換並等待另一個執行緒呼叫exchange,並且設定最大等待時間,當等待時間超過timeout便停止等待。

2.例項講解

        通過以上的原理,可以知道使用Exchanger類的核心便是exchange()方法的使用,接下來通過一個例子來使的該工具類的用途更加清晰。該例子主要講解的是前段時間NBA交易截止日的交易。

package concurrent;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.*;
public class ExchangerDemo {
	
	public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        
        final Exchanger exchanger = new Exchanger();
        executor.execute(new Runnable() {
            String data1 = "克拉克森,小拉里南斯";
            

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        

        executor.execute(new Runnable() {
            String data1 = "格里芬";

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        
        executor.execute(new Runnable() {
            String data1 = "哈里斯";

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        
        executor.execute(new Runnable() {
            String data1 = "以賽亞托馬斯,弗萊";

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        
        executor.shutdown();
    }

    private static void nbaTrade(String data1, Exchanger exchanger) {
        try {
            System.out.println(Thread.currentThread().getName() + "在交易截止之前把 " + data1 + " 交易出去");
            Thread.sleep((long) (Math.random() * 1000));

            String data2 = (String) exchanger.exchange(data1);
            System.out.println(Thread.currentThread().getName() + "交易得到" + data2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

執行程式,得到如下結果:

pool-1-thread-1在交易截止之前把 克拉克森,小拉里南斯 交易出去
pool-1-thread-2在交易截止之前把 格里芬 交易出去
pool-1-thread-3在交易截止之前把 哈里斯 交易出去
pool-1-thread-4在交易截止之前把 以賽亞托馬斯,弗萊 交易出去
pool-1-thread-2交易得到哈里斯
pool-1-thread-3交易得到格里芬
pool-1-thread-4交易得到克拉克森,小拉里南斯
pool-1-thread-1交易得到以賽亞托馬斯,弗萊

        以上例子可以看出兩個都呼叫exchange()方法的執行緒會進行交換資料。接下來假設執行緒數目只有奇數個,觀察情況:

如以下程式碼,將第四個執行緒註釋掉。

package concurrent;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.*;
public class ExchangerDemo {
	
	public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        
        final Exchanger exchanger = new Exchanger();
        executor.execute(new Runnable() {
            String data1 = "克拉克森,小拉里南斯";
            

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        

        executor.execute(new Runnable() {
            String data1 = "格里芬";

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        
        executor.execute(new Runnable() {
            String data1 = "哈里斯";

            @Override
            public void run() {
                nbaTrade(data1, exchanger);
            }
        });
        
//        executor.execute(new Runnable() {
//            String data1 = "以賽亞托馬斯,弗萊";
//
//            @Override
//            public void run() {
//                nbaTrade(data1, exchanger);
//            }
//        });
        
        executor.shutdown();
    }

    private static void nbaTrade(String data1, Exchanger exchanger) {
        try {
            System.out.println(Thread.currentThread().getName() + "在交易截止之前把 " + data1 + " 交易出去");
            Thread.sleep((long) (Math.random() * 1000));

            String data2 = (String) exchanger.exchange(data1);
            System.out.println(Thread.currentThread().getName() + "交易得到" + data2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

執行程式,得到如下結果:

pool-1-thread-1在交易截止之前把 克拉克森,小拉里南斯 交易出去
pool-1-thread-2在交易截止之前把 格里芬 交易出去
pool-1-thread-3在交易截止之前把 哈里斯 交易出去
pool-1-thread-3交易得到格里芬
pool-1-thread-2交易得到哈里斯

        由結果可知,執行緒2和執行緒3進行了交換資料,而執行緒1一直等待與它交換資料的執行緒呼叫exchange,但是隻有3個執行緒,所以會一直等待。

        因此,當兩個執行緒之間出現資料交換的情況,可以使用Exchanger工具類實現資料交換。注意exchange方法的含義,以及觸發資料交換的條件。

相關文章