Java并发编程实战笔记 (第五章--2)

5.5.2 FutureTask
FutureTask也可以用作闭锁。(FutureTask实现了Future语义,表示一种抽象的可生成结果的计算)。FutureTask表示的计算是通过Callable来实现的,相当于一种可生成结果的Runnable,有三种状态:执行完成【正常结束、取消、异常】。

Future.get的行为取决于任务的状态。如果任务已经完成,那么get会立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常。

// 支持泛型
@FunctionInterface
public interface Callable<V>{
	V call() throws Exception;
}

Callable一般是配合线程池工具ExecutorService来使用。
Future接口只有几个简单的方法;

public abstract interface Future<V>{
	public abstract boolean cancel(boolean paramBoolean);
	public abstract boolean isCancel();
	public abstract boolean isDone();
	public abstract V get() throws InterruptedException, ExecutionException;
	public  abstract V get(long paramLong, TimeUnit paramTimeUnit)
		throws InterruptedException, ExecutionException, TimeoutException;
}

注意:cancel方法只是试图取消一个线程的执行,并不一定能够取消成功。
有时候,为了让任务有能够取消的功能,就使用Callable来代替Runnable,可以声明Future<?>形式类型,并返回null作为底层任务的结果。

FutureTask实现的RunnableFuture接口,而RunnableFuture接口又同时继承了Runnable接口和Future接口:

public interface RunnableFuture<V> extends Runnable, Future<V>{
	void run();
}

由于Future只是一个接口,而它里面的cancel、get、isDone等方法要自己实现起来都是很复杂的,所以JDK提供了一个FutureTask类供使用。

class Task implements Callable<Integer>{
	@override
	public Integer call() throws Exception{
		Thread.sleep(1000);
		return 2;
	}
	public static void main(String ars[]){
		ExecutorService executor = Executor.newCachedThreadPool();
		Task task = new Task();
		Future<Integer> result = executor.submit(task); // 这个方法执行有返回值
		//调用get方法会阻塞当前的线程,直到得到结果
		// 所有实际编码中建议可以设置超时间的重载get方法。
		System.out.println(result.get());
	}
}
~output:
2

// 与上面的区别:调用submit方法是没有返回值的,这里实际调用的submit(Runnable task)方法,而上面的Demo,调用的是submit(Callable<T> task)方法,在许多高并发的环境下,有可能Callable和FutureTask会创建多次,FutureTask能够在高并发环境下确保任务只执行一次。
class Task implements Callable<Integer>{
	@Override
	public Integer call() throws Exception{
		Thread.sleep(1000);
		return 2;
	}
	public static void main(String args[]){
		ExecutorService executor = Executor.newCachedThreadPool();
		FutureTask<Integer> futureTask = new FutureTask<>(new Task());
		executor.submit(futureTask); // 这个方法没有返回值
		System.out.println(futureTask.get());
	}
}

5.5.3 信号量
计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的次数。计数信号量还可以用来实现某种资源池,或者对容器施加边界。可以使用volatile变量来实现“信号量”的模型。
应用场景:
多个线程(超过2个)需要相互合作,我们用简单的“锁”和“等待通知机制”就不那么方便了。这个时候就可以用到信号量。在JDK中提供的很多线程通信工具类都是基于信号量模型的。

package chapter05;

public class Signal {
    private static volatile int signal = 0;

    static class ThreadA implements Runnable{
        @Override
        public void run() {
            while(signal < 5){
                if (signal % 2 == 0){
                    System.out.println("threadA: " + signal);
                    synchronized (this){
                        signal ++;
                    }
                }
            }
        }
    }

    static class ThreadB implements Runnable{
        @Override
        public void run() {
            while(signal < 5){
                if (signal % 2 == 1){
                    System.out.println("threadB: " + signal);
                    synchronized (this){
                        signal = signal +1;
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        new Thread(new ThreadA()).start();
        Thread.sleep(1000);
        new Thread(new ThreadB()).start();
    }
}

output~:
threadA: 0
threadB: 1
threadA: 2
threadB: 3
threadA: 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值