Java multithreaded learning - java.util.concurrent (2) Semaphore/FutureTask/Exchanger

Keywords: Java JDK Programming

Java multithreaded learning - java.util.concurrent (2) Semaphore/FutureTask/Exchanger


3. Semaphore 

Let's start with a detailed introduction to this class in the JDK 1.5 API:
"A counting semaphore. Conceptually, semaphores maintain a license set. If necessary, each acquire() is blocked before the license is available, and then the license is obtained. Each release() adds a license, which may release a blocking acquirer. However, without using the actual license object, Semaphore only counts the number of licenses available and takes corresponding actions."  

We usually use it to control thread access objects of an object.

For example, for a container, we specify that at most n threads can operate simultaneously.
Use signal quantities to simulate implementation

The code is as follows (refer to [JCIP])


import java.util.Collections;  
import java.util.HashSet;  
import java.util.Set;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Semaphore;  
  
public class TestSemaphore {  
  
    public static void main(String[] args) {  
        ExecutorService exec = Executors.newCachedThreadPool();  
        TestSemaphore t = new TestSemaphore();  
        final BoundedHashSet<String> set = t.getSet();  
  
        for (int i = 0; i < 3; i++) {//Three threads operate add at the same time  
            exec.execute(new Runnable() {  
                public void run() {  
                    try {  
                        set.add(Thread.currentThread().getName());  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            });  
        }  
  
        for (int j = 0; j < 3; j++) {//Three threads operate remove at the same time  
            exec.execute(new Runnable() {  
                public void run() {  
                    set.remove(Thread.currentThread().getName());  
                }  
            });  
        }  
        exec.shutdown();  
    }  
  
    public BoundedHashSet<String> getSet() {  
        return new BoundedHashSet<String>(2);//Define a thread with boundary constraint 2  
    }  
  
    class BoundedHashSet<T> {  
        private final Set<T> set;  
        private final Semaphore semaphore;  
  
        public BoundedHashSet(int bound) {  
            this.set = Collections.synchronizedSet(new HashSet<T>());  
            this.semaphore = new Semaphore(bound, true);  
        }  
  
        public void add(T o) throws InterruptedException {  
            semaphore.acquire();//Semaphore controls the number of accessible threads  
            set.add(o);  
            System.out.printf("add:%s%n",o);  
        }  
  
        public void remove(T o) {  
            if (set.remove(o))  
                semaphore.release();//Release the semaphore  
            System.out.printf("remove:%s%n",o);  
        }  
    }  
}  

Summary: Semaphore is commonly used for object pool control


4.FutureTask 
Let's start with a detailed introduction to this class in the JDK 1.5 API:

Canceled asynchronous computing. The basic implementation of Future is provided by using the methods of starting and canceling calculation, querying whether calculation is completed or not, and obtaining calculation results. The result can be obtained only when the calculation is completed; if the calculation is not completed, the get method is blocked. Once the calculation is completed, the calculation cannot be restarted or cancelled.  
Callable or Runnable objects can be wrapped using FutureTask. Because FutureTask implements Runnable, FutureTask can be submitted to Executor for execution.  
In addition to being a separate class, this class also provides protected functionality, which may be useful in creating custom task classes. "

Application example: Our algorithm has a time-consuming operation. In programming, we want to separate it into a module and call it as it returns immediately and can be cancelled at any time.

The code is as follows (refer to [JCIP])

import java.util.concurrent.Callable;  
import java.util.concurrent.ExecutionException;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.FutureTask;  
  
public class TestFutureTask {  
  
    public static void main(String[] args) {  
        ExecutorService exec=Executors.newCachedThreadPool();  
          
        FutureTask<String> task=new FutureTask<String>(new Callable<String>(){//FutrueTask's construction parameter is a Callable interface  
            @Override  
            public String call() throws Exception {  
                return Thread.currentThread().getName();//This can be an asynchronous operation  
            }});  
              
            try {  
                exec.execute(task);//FutureTask is actually a thread  
                String result=task.get();//Get the result of the asynchronous calculation, and if it does not return, it will always block and wait.  
                System.out.printf("get:%s%n",result);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } catch (ExecutionException e) {  
                e.printStackTrace();  
            }  
    }  
  
}  

Summary: FutureTask actually creates a new thread to execute independently, so that the thread has a return value, which is convenient for programming.

5. Exchanger 
Let's start with a detailed introduction to this class in the JDK 1.5 API:
"Synchronization points of threads that can pair and exchange elements in pairs. Each thread presents a method on an entry to the exchange method, matches the partner thread, and receives the partner's object on return. Exchanger may be considered a two-way form of SynchronousQueue. Exchanger may be useful in applications such as genetic algorithms and pipeline design. "

Application example: There are two caches, two threads fill and take to two caches respectively, if and only if one is full, two caches exchange.

The code is as follows (referring to the example given online: http://hi.baidu.com/webidea/blog/item/2995e731e53ad5a55fdf0e7d.html)

import java.util.ArrayList;  
import java.util.concurrent.Exchanger;  
  
public class TestExchanger {  
  
    public static void main(String[] args) {  
        final Exchanger<ArrayList<Integer>> exchanger = new Exchanger<ArrayList<Integer>>();  
        final ArrayList<Integer> buff1 = new ArrayList<Integer>(10);  
        final ArrayList<Integer> buff2 = new ArrayList<Integer>(10);  
  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                ArrayList<Integer> buff = buff1;  
                try {  
                    while (true) {  
                        if (buff.size() >= 10) {  
                            buff = exchanger.exchange(buff);//Start interacting with another thread  
                            System.out.println("exchange buff1");  
                            buff.clear();  
                        }  
                        buff.add((int)(Math.random()*100));  
                        Thread.sleep((long)(Math.random()*1000));  
                    }  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
          
        new Thread(new Runnable(){  
            @Override  
            public void run() {  
                ArrayList<Integer> buff=buff2;  
                while(true){  
                    try {  
                        for(Integer i:buff){  
                            System.out.println(i);  
                        }  
                        Thread.sleep(1000);  
                        buff=exchanger.exchange(buff);//Start exchanging data with another thread  
                        System.out.println("exchange buff2");  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }}).start();  
    }  
}  
Summary: Exchanger is useful in specific usage scenarios (data interaction between two partner threads)

Posted by Mercenary on Sat, 22 Dec 2018 23:51:05 -0800