java多线程学习-java.util.concurrent详解(二)Semaphore/FutureTask/Exchanger

  我们学习了java.util.concurrent的CountDownLatch和CyclicBarrier
今天我们继续共同来探讨其他的多线程组件
-----------------------------------------------------------------------------
3. Semaphore
    我们先来学习一下JDK1.5 API中关于这个类的详细介绍:
“一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。”

    我们一般用它来控制某个对象的线程访问对象

    例如 ,对于某个容器,我们规定,最多只能容纳n个线程同时操作
使用信号量来模拟实现


具体代码如下(参考 [JCIP])
Java代码 复制代码  收藏代码
  1. import java.util.Collections;   
  2. import java.util.HashSet;   
  3. import java.util.Set;   
  4. import java.util.concurrent.ExecutorService;   
  5. import java.util.concurrent.Executors;   
  6. import java.util.concurrent.Semaphore;   
  7.   
  8. public class TestSemaphore {   
  9.   
  10.     public static void main(String[] args) {   
  11.         ExecutorService exec = Executors.newCachedThreadPool();   
  12.         TestSemaphore t = new TestSemaphore();   
  13.         final BoundedHashSet<String> set = t.getSet();   
  14.   
  15.         for (int i = 0; i < 3; i++) {//三个线程同时操作add   
  16.             exec.execute(new Runnable() {   
  17.                 public void run() {   
  18.                     try {   
  19.                         set.add(Thread.currentThread().getName());   
  20.                     } catch (InterruptedException e) {   
  21.                         e.printStackTrace();   
  22.                     }   
  23.                 }   
  24.             });   
  25.         }   
  26.   
  27.         for (int j = 0; j < 3; j++) {//三个线程同时操作remove   
  28.             exec.execute(new Runnable() {   
  29.                 public void run() {   
  30.                     set.remove(Thread.currentThread().getName());   
  31.                 }   
  32.             });   
  33.         }   
  34.         exec.shutdown();   
  35.     }   
  36.   
  37.     public BoundedHashSet<String> getSet() {   
  38.         return new BoundedHashSet<String>(2);//定义一个边界约束为2的线程   
  39.     }   
  40.   
  41.     class BoundedHashSet<T> {   
  42.         private final Set<T> set;   
  43.         private final Semaphore semaphore;   
  44.   
  45.         public BoundedHashSet(int bound) {   
  46.             this.set = Collections.synchronizedSet(new HashSet<T>());   
  47.             this.semaphore = new Semaphore(bound, true);   
  48.         }   
  49.   
  50.         public void add(T o) throws InterruptedException {   
  51.             semaphore.acquire();//信号量控制可访问的线程数目   
  52.             set.add(o);   
  53.             System.out.printf("add:%s%n",o);   
  54.         }   
  55.   
  56.         public void remove(T o) {   
  57.             if (set.remove(o))   
  58.                 semaphore.release();//释放掉信号量   
  59.             System.out.printf("remove:%s%n",o);   
  60.         }   
  61.     }   
  62. }  
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++) {//三个线程同时操作add
			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++) {//三个线程同时操作remove
			exec.execute(new Runnable() {
				public void run() {
					set.remove(Thread.currentThread().getName());
				}
			});
		}
		exec.shutdown();
	}

	public BoundedHashSet<String> getSet() {
		return new BoundedHashSet<String>(2);//定义一个边界约束为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();//信号量控制可访问的线程数目
			set.add(o);
			System.out.printf("add:%s%n",o);
		}

		public void remove(T o) {
			if (set.remove(o))
				semaphore.release();//释放掉信号量
			System.out.printf("remove:%s%n",o);
		}
	}
}


    总结:Semaphore通常用于对象池的控制

4.FutureTask
    我们先来学习一下JDK1.5 API中关于这个类的详细介绍:

    “取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。
可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
除了作为一个独立的类外,此类还提供了 protected 功能,这在创建自定义任务类时可能很有用。 “

    应用举例: 我们的算法中有一个很耗时的操作,在编程的是,我们希望将它独立成一个模块,调用的时候当做它是立刻返回的,并且可以随时取消的

具体代码如下(参考 [JCIP])
Java代码 复制代码  收藏代码
  1. import java.util.concurrent.Callable;   
  2. import java.util.concurrent.ExecutionException;   
  3. import java.util.concurrent.ExecutorService;   
  4. import java.util.concurrent.Executors;   
  5. import java.util.concurrent.FutureTask;   
  6.   
  7. public class TestFutureTask {   
  8.   
  9.     public static void main(String[] args) {   
  10.         ExecutorService exec=Executors.newCachedThreadPool();   
  11.            
  12.         FutureTask<String> task=new FutureTask<String>(new Callable<String>(){//FutrueTask的构造参数是一个Callable接口   
  13.             @Override  
  14.             public String call() throws Exception {   
  15.                 return Thread.currentThread().getName();//这里可以是一个异步操作   
  16.             }});   
  17.                
  18.             try {   
  19.                 exec.execute(task);//FutureTask实际上也是一个线程   
  20.                 String result=task.get();//取得异步计算的结果,如果没有返回,就会一直阻塞等待   
  21.                 System.out.printf("get:%s%n",result);   
  22.             } catch (InterruptedException e) {   
  23.                 e.printStackTrace();   
  24.             } catch (ExecutionException e) {   
  25.                 e.printStackTrace();   
  26.             }   
  27.     }   
  28.   
  29. }  
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的构造参数是一个Callable接口
			@Override
			public String call() throws Exception {
				return Thread.currentThread().getName();//这里可以是一个异步操作
			}});
			
			try {
				exec.execute(task);//FutureTask实际上也是一个线程
				String result=task.get();//取得异步计算的结果,如果没有返回,就会一直阻塞等待
				System.out.printf("get:%s%n",result);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
	}

}


    总结:FutureTask其实就是新建了一个线程单独执行,使得线程有一个返回值,方便程序的编写

5. Exchanger
    我们先来学习一下JDK1.5 API中关于这个类的详细介绍:
    “可以在pair中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。Exchanger 可能被视为 SynchronousQueue 的双向形式。Exchanger 可能在应用程序(比如遗传算法和管道设计)中很有用。 “

    应用举例: 有两个缓存区,两个线程分别向两个缓存区fill和take,当且仅当一个满了,两个缓存区交换

    代码如下(参考了网上给的示例   http://hi.baidu.com/webidea/blog/item/2995e731e53ad5a55fdf0e7d.html)
Java代码 复制代码  收藏代码
  1. import java.util.ArrayList;   
  2. import java.util.concurrent.Exchanger;   
  3.   
  4. public class TestExchanger {   
  5.   
  6.     public static void main(String[] args) {   
  7.         final Exchanger<ArrayList<Integer>> exchanger = new Exchanger<ArrayList<Integer>>();   
  8.         final ArrayList<Integer> buff1 = new ArrayList<Integer>(10);   
  9.         final ArrayList<Integer> buff2 = new ArrayList<Integer>(10);   
  10.   
  11.         new Thread(new Runnable() {   
  12.             @Override  
  13.             public void run() {   
  14.                 ArrayList<Integer> buff = buff1;   
  15.                 try {   
  16.                     while (true) {   
  17.                         if (buff.size() >= 10) {   
  18.                             buff = exchanger.exchange(buff);//开始跟另外一个线程交互数据   
  19.                             System.out.println("exchange buff1");   
  20.                             buff.clear();   
  21.                         }   
  22.                         buff.add((int)(Math.random()*100));   
  23.                         Thread.sleep((long)(Math.random()*1000));   
  24.                     }   
  25.                 } catch (InterruptedException e) {   
  26.                     e.printStackTrace();   
  27.                 }   
  28.             }   
  29.         }).start();   
  30.            
  31.         new Thread(new Runnable(){   
  32.             @Override  
  33.             public void run() {   
  34.                 ArrayList<Integer> buff=buff2;   
  35.                 while(true){   
  36.                     try {   
  37.                         for(Integer i:buff){   
  38.                             System.out.println(i);   
  39.                         }   
  40.                         Thread.sleep(1000);   
  41.                         buff=exchanger.exchange(buff);//开始跟另外一个线程交换数据   
  42.                         System.out.println("exchange buff2");   
  43.                     } catch (InterruptedException e) {   
  44.                         e.printStackTrace();   
  45.                     }   
  46.                 }   
  47.             }}).start();   
  48.     }   
  49. }  
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);//开始跟另外一个线程交互数据
							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);//开始跟另外一个线程交换数据
						System.out.println("exchange buff2");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}}).start();
	}
}



    总结:Exchanger在特定的使用场景比较有用(两个伙伴线程之间的数据交互)
当在使用Java中的FutureTask类时,可能会遇到java.util.concurrent.FutureTask.report内存溢出的问题。 FutureTaskJavaExecutor框架的一部分,用于表示一个异步计算任务。在执行计算任务时,可能会出现内存溢出的情况,这通常是由于以下原因引起的: 1. 任务计算量过大:如果计算任务需要处理大量的数据或执行复杂的操作,可能会导致内存使用过多,进而导致内存溢出。 2. 内存泄漏:如果在任务执行过程中有未正确释放的资源或对象引用,那么这些未释放的资源可能会一直保存在内存中,最终导致内存溢出。 解决这个问题的方法如下: 1. 优化任务计算:检查计算任务的实现代码,尝试减少计算量或改进计算逻辑,以降低内存使用量。可以通过合理使用数据结构、循环和递归等技巧来减少内存占用。 2. 调整内存配置:如果计算任务确实需要较大的内存空间,可以调整Java虚拟机的内存配置参数。可以增加-Xmx和-Xms等参数,以提供足够的内存空间给计算任务使用。 3. 检查内存泄漏:通过使用Java内存分析工具,如Eclipse Memory Analyzer等,来检查任务执行期间是否存在内存泄漏。如果发现泄漏问题,需要修复代码,确保资源和对象在不再使用时能够正确释放。 综上所述,当出现java.util.concurrent.FutureTask.report内存溢出的情况时,我们可以通过优化任务计算、调整内存配置和检查内存泄漏等方法来解决该问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值