Callable的简单说明及使用

本文介绍了如何使用Java的Callable接口在并发环境中创建任务,并关注了FutureTask的使用和结果缓存现象。通过实例展示了如何在MyThread类中实现Callable并启动多个线程,结果只输出一次,因为FutureTask存在线程缓存机制。

Callable的简单实现
在这里插入图片描述


    package com.itheima.callable;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class CallableTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //new Thread(new Future<V>(Callable)).start();
            MyThread thread=new MyThread();
            FutureTask integerFutureTask = new FutureTask(thread);  //适配器
            new Thread(integerFutureTask,"A").start();
            new Thread(integerFutureTask,"B").start();//只输出一个call是因为其有缓存
    
            Integer o = (Integer) integerFutureTask.get(); //获得Callable的返回值结果
            System.out.println(o);
    
    
        }
    }
    class MyThread implements Callable<Integer>{
    
        @Override
        public Integer call() throws Exception {
            System.out.println("call()");
            return 1024;
        }
    }

注意:
1.当有多个线程时,输出结果有缓存。
2.结果可能需要等待,因为get()方法耗时较长,也可能被阻塞。

<think>我们使用Callable接口来模拟小红和小明随机赠送礼物的过程。由于礼物赠送是随机进行的(即每次随机选择一个人赠送),我们需要一个共享的礼物计数器,并且需要保证线程安全(因为两个线程会同时修改礼物数量)。我们可以使用AtomicInteger来保证原子操作,或者使用锁机制。但注意,由于是随机赠送,我们不需要交替进行,而是每个线程独立随机赠送,直到礼物少于10个为止。 实现思路: 1. 定义一个共享的礼物数量(初始为100),使用AtomicInteger保证线程安全。 2. 创建两个Callable任务,分别代表小红和小明。每个任务在礼物数量大于等于10时,随机等待一段时间(模拟随机性),然后尝试赠送一个礼物(使用原子操作减少礼物数量,并记录自己赠送的数量)。 3. 当礼物数量小于10时,任务结束,返回各自赠送的数量。 4. 使用FutureTask来获取每个任务的结果。 注意:由于两个线程会同时竞争礼物数量,我们需要确保每次减少礼物数量是原子操作。因此,我们使用AtomicInteger的decrementAndGet()方法,但要注意在减少之前需要检查礼物数量是否大于等于10(因为可能两个线程同时检查都满足,然后都减少,导致礼物数量变成负数)。因此,我们使用一个循环,在原子操作中检查并减少。 具体步骤: while (当前礼物数量 >= 10) { 尝试原子减少:先获取当前值,如果>=10,则减1,否则放弃。 如果减少成功,则记录自己赠送的数量,并休息随机时间。 如果减少失败(因为其他线程已经让礼物数量小于10了),则退出。 } 但是,AtomicInteger的原子操作不能同时满足“检查并减少”的复合操作,我们可以使用compareAndSet(expectedValue, newValue)来实现。不过,更简单的方法是使用AtomicInteger的getAndUpdate方法,但这里我们使用循环CAS。 另一种方法是使用一个循环: int current; do { current = giftCount.get(); if (current < 10) break; } while (!giftCount.compareAndSet(current, current-1)); 这样,如果当前值大于等于10,我们就尝试将其减1,如果成功则赠送成功,否则重试(因为其他线程可能修改了礼物数量)。 由于我们要求每个线程记录自己赠送的数量,所以每个线程内部有一个计数器,每次成功赠送就加1。 另外,为了模拟随机性,每次赠送后让线程睡眠一个随机时间。 代码结构: - 共享的礼物数量:AtomicInteger giftCount = new AtomicInteger(100); - 每个Callable任务返回自己赠送的数量。 但是,注意:当礼物数量从10减少到9时,应该停止。所以我们在循环中不断尝试,直到礼物数量小于10。 由于两个线程会同时竞争,所以可能会出现一个线程在赠送后礼物数量变为9,另一个线程在赠送前检查时还是10,但尝试减少时(使用CAS)会失败,因为此时礼物数量已经变为9(或其他值),所以会失败,然后退出。 因此,每个任务代码类似: int myCount = 0; while (true) { int current = giftCount.get(); if (current < 10) { break; } // 尝试减少1 if (giftCount.compareAndSet(current, current - 1)) { myCount++; // 模拟随机时间 Thread.sleep((long) (Math.random() * 100)); } // 否则,重试(因为其他线程可能修改了,但礼物数量可能还是>=10,所以重试) } return myCount; 但是,这样如果竞争激烈,重试次数会很多。不过,由于我们每次赠送后都睡眠随机时间,所以竞争不会太激烈。 另外,我们也可以使用显式锁(Lock)来保证,但这里为了简单使用无锁的CAS操作。 最后,主线程等待两个任务结束,然后打印结果。 完整代码:</think># 使用Callable实现小红和小明随机赠送礼物 我将使用Callable接口实现多线程模拟小红和小明随机赠送礼物的过程,并统计各自送出的礼物数量。以下是完整的解决方案: ```java import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; class GiftExchange { // 共享的礼物计数器(原子操作保证线程安全) private static AtomicInteger gifts = new AtomicInteger(100); // 小红和小明的赠送计数器 private static AtomicInteger redCount = new AtomicInteger(0); private static AtomicInteger mingCount = new AtomicInteger(0); // 随机数生成器 private static Random random = new Random(); // 小红赠送任务 static class RedGiftTask implements Callable<Integer> { @Override public Integer call() throws Exception { while (gifts.get() >= 10) { // 随机延迟模拟赠送过程 Thread.sleep(random.nextInt(50) + 20); // 获取当前礼物数量 int currentGifts = gifts.get(); if (currentGifts < 10) break; // 原子操作减少礼物数量 if (gifts.compareAndSet(currentGifts, currentGifts - 1)) { redCount.incrementAndGet(); System.out.println("小红赠送礼物,剩余: " + (currentGifts - 1)); } } return redCount.get(); } } // 小明赠送任务 static class MingGiftTask implements Callable<Integer> { @Override public Integer call() throws Exception { while (gifts.get() >= 10) { // 随机延迟模拟赠送过程 Thread.sleep(random.nextInt(50) + 20); // 获取当前礼物数量 int currentGifts = gifts.get(); if (currentGifts < 10) break; // 原子操作减少礼物数量 if (gifts.compareAndSet(currentGifts, currentGifts - 1)) { mingCount.incrementAndGet(); System.out.println("小明赠送礼物,剩余: " + (currentGifts - 1)); } } return mingCount.get(); } } public static void main(String[] args) { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(2); try { // 提交任务 Future<Integer> redFuture = executor.submit(new RedGiftTask()); Future<Integer> mingFuture = executor.submit(new MingGiftTask()); // 获取任务结果 int redResult = redFuture.get(); int mingResult = mingFuture.get(); // 打印最终结果 System.out.println("\n===== 礼物赠送统计 ====="); System.out.println("小红赠送数量: " + redResult); System.out.println("小明赠送数量: " + mingResult); System.out.println("剩余礼物: " + gifts.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } ``` ## 实现原理说明 1. **Callable接口使用**: - 创建`RedGiftTask`和`MingGiftTask`两个任务类实现`Callable`接口 - 在`call()`方法中实现赠送逻辑 - 使用`Future`对象获取任务执行结果[^1] 2. **线程安全处理**: - 使用`AtomicInteger`保证礼物计数和赠送计数的原子操作 - 通过`compareAndSet()`实现安全的并发减少操作[^2] - 避免使用`synchronized`带来的性能开销[^3] 3. **随机赠送模拟**: - 使用`Thread.sleep()`添加随机延迟 - 随机延迟范围在20-70毫秒之间 - 模拟真实场景中赠送速度的差异 4. **终止条件**: - 当礼物数量小于10时停止赠送 - 使用`while (gifts.get() >= 10)`作为循环条件 - 每次操作前检查当前礼物数量 ## 执行结果示例 程序执行将输出类似以下结果(由于随机性,每次运行结果可能不同): ``` 小红赠送礼物,剩余: 99 小明赠送礼物,剩余: 98 小明赠送礼物,剩余: 97 小红赠送礼物,剩余: 96 ... 小红赠送礼物,剩余: 10 小明赠送礼物,剩余: 9 ===== 礼物赠送统计 ===== 小红赠送数量: 47 小明赠送数量: 44 剩余礼物: 9 ``` ## 性能优化考虑 1. **线程池管理**: - 使用`ExecutorService`管理线程资源 - 避免频繁创建销毁线程的开销 - 合理设置线程池大小[^4] 2. **CAS操作优势**: - `compareAndSet()`比锁机制更轻量 - 减少线程阻塞时间 - 提高并发性能 3. **随机性设计**: - 不同线程的随机延迟不同 - 模拟真实场景中速度差异 - 避免固定顺序的赠送模式 ## 相关问题 ### 1. 如何修改代码实现三人交替赠送礼物? 要实现三人交替赠送,可以使用循环屏障(CyclicBarrier)或轮询机制。核心是引入一个共享的计数器,每个线程在赠送前检查是否轮到自己: ```java private static AtomicInteger turn = new AtomicInteger(0); // 在call()方法中添加 if (turn.get() % 3 != playerId) continue; turn.incrementAndGet(); ``` ### 2. 如果礼物赠送需要耗时操作(如网络请求),如何优化线程等待策略? 对于耗时操作,可以: 1. 使用`CompletableFuture`实现异步非阻塞 2. 增加线程池大小 3. 使用回调机制替代同步等待 4. 实现超时重试机制 ### 3. 如何用`Lock`和`Condition`替代`synchronized`实现相同功能? 使用`ReentrantLock`和`Condition`的示例: ```java private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); // 在赠送操作中 lock.lock(); try { if (gifts >= 10) { // 赠送操作 condition.signalAll(); } } finally { lock.unlock(); } ``` ### 4. 当礼物数量达到临界值(如10)时,如何防止多赠送一次? 使用双检查机制确保安全: ```java int current; do { current = gifts.get(); if (current < 10) break; } while (!gifts.compareAndSet(current, current - 1)); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值