当前位置: 首页 > news >正文

JUC的常见类

JUC的常见类

1. Callable与Runnable接口
Callable接口与Runnable接口的主要区别在于:Callable的call()方法可以返回执行结果并抛出受检异常,而Runnable的run()方法既不能返回结果也不能抛出受检异常。这使得Callable更适合需要获取计算结果或处理复杂异常的场景。
1.1 Runnable的使用
方法一:实现Runnable接口

// 实现 Runnable 接口
class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class RunnableExample {public static void main(String[] args) {// 创建 Runnable 实例MyRunnable myRunnable = new MyRunnable();// 创建线程并启动Thread thread1 = new Thread(myRunnable, "线程1");Thread thread2 = new Thread(myRunnable, "线程2");thread1.start();thread2.start();}
}

方法二:使用匿名内部类

public class AnonymousRunnable {public static void main(String[] args) {// 使用匿名内部类Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 3; i++) {System.out.println("匿名内部类线程: " + i);}}});thread.start();}
}

方法三:使用 Lambda 表达式

public class LambdaRunnable {public static void main(String[] args) {// 使用 Lambda 表达式Thread thread = new Thread(() -> {for (int i = 0; i < 3; i++) {System.out.println("Lambda线程: " + i);}});thread.start();// 更简洁的写法new Thread(() -> System.out.println("简洁的Lambda线程")).start();}
}

方法四:使用 ExecutorService 执行 Runnable

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorServiceExample {public static void main(String[] args) {// 创建线程池ExecutorService executor = Executors.newFixedThreadPool(3);// 提交多个任务for (int i = 0; i < 5; i++) {final int taskId = i;executor.execute(() -> {System.out.println("执行任务 " + taskId + " 在线程: " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

1.2 Callable的使用
方法一:使用Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;// 实现 Callable 接口
class MyCallable implements Callable<String> {private final String name;public MyCallable(String name) {this.name = name;}@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName() + " 开始执行任务: " + name);Thread.sleep(2000); // 模拟耗时操作System.out.println(Thread.currentThread().getName() + " 完成任务: " + name);return "任务 " + name + " 的执行结果";}
}public class CallableExample {public static void main(String[] args) throws Exception {// 创建 Callable 实例Callable<String> callable = new MyCallable("测试任务");// 使用 FutureTask 包装 CallableFutureTask<String> futureTask = new FutureTask<>(callable);// 创建线程并启动Thread thread = new Thread(futureTask);thread.start();// 获取结果(会阻塞直到任务完成)String result = futureTask.get();System.out.println("任务结果: " + result);}
}

方法二:使用Lambda表达式

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class LambdaCallable {public static void main(String[] args)throws Exception {// 使用 Lambda 表达式创建 CallableCallable<Integer> callable = () -> {int sum = 0;for (int i = 1; i <= 100; i++) {sum += i;Thread.sleep(10); // 模拟计算过程}return sum;};FutureTask<Integer> futureTask = new FutureTask<>(callable);new Thread(futureTask).start();// 可以在这里执行其他任务...System.out.println("等待计算结果中...");Integer result = futureTask.get();System.out.println("1-100的和为: " + result); // 输出: 5050}
}

方法三:使用 ExecutorService 执行 Callable

import java.util.concurrent.*;
import java.util.*;public class ExecutorServiceCallable {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);// 创建多个 Callable 任务List<Callable<String>> tasks = new ArrayList<>();for (int i = 1; i <= 5; i++) {final int taskId = i;tasks.add(() -> {System.out.println("执行任务 " + taskId + " 在线程: " + Thread.currentThread().getName());Thread.sleep(1000);return "任务 " + taskId + " 完成";});}try {// 提交所有任务并获取 Future 列表List<Future<String>> futures = executor.invokeAll(tasks);// 获取所有任务结果for (Future<String> future : futures) {try {String result = future.get();System.out.println("结果: " + result);} catch (ExecutionException e) {System.out.println("任务执行异常: " + e.getMessage());}}} catch (InterruptedException e) {e.printStackTrace();} finally {executor.shutdown();}}
}

1.3 理解Callable
代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 不使⽤ Callable 版本

    //创建⼀个类 Result , 包含⼀个 sum 表⽰最终结果, lock 表⽰线程同步使⽤的锁对象static class Result {public int sum;public Object lock;}//main ⽅法中先创建 Result 实例, 然后创建⼀个线程 t. 在线程内部计算 1 + 2 + 3 + ... + 1000.public static void main(String[] args) throws InterruptedException {Result result = new Result();Thread t = new Thread() {//当线程 t 计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果@Overridepublic void run() {int sum = 0;for (int i = 1; i <= 1000; i++) {sum += i;}synchronized (result.lock) {result.sum = sum;result.lock.notify();}}};t.start();//主线程同时使⽤ wait 等待线程 t 计算结束. (注意, 如果执⾏到 wait 之前, 线程 t 已经计算完了, 就不//必等待了).synchronized (result.lock) {while (result.sum == 0) {result.lock.wait();}System.out.println(result.sum);}}

代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 使⽤ Callable 版本

    public static void main(String[] args) throws Exception {//创建⼀个匿名内部类, 实现 Callable 接⼝. Callable 带有泛型参数. 泛型参数表⽰返回值的类型Callable<Integer> callable = new Callable<Integer>() {//重写 Callable 的 call ⽅法, 完成累加的过程. 直接通过返回值返回计算结果@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 1000; i++) {sum += i;}return sum;}};//把 callable 实例使⽤ FutureTask 包装⼀下FutureTask<Integer> futureTask = new FutureTask<>(callable);//创建线程, 线程的构造⽅法传⼊ FutureTask . 此时新线程就会执⾏ FutureTask 内部的 Callable 的//call ⽅法, 完成计算. 计算结果就放到了 FutureTask 对象中Thread t = new Thread(futureTask);t.start();//在主线程中调⽤ futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的//结果int result = futureTask.get();System.out.println(result);}
  • Callable 和 Runnable 相对, 都是描述⼀个 “任务”. Callable 描述的是带有返回值的任务, Runnable
  • 描述的是不带返回值的任务.
  • Callable 通常需要搭配 FutureTask 来使⽤. FutureTask ⽤来保存Callable 的返回结果. 因为Callable 往往是在另⼀个线程中执⾏的, 啥时候执⾏完并不确定.
  • FutureTask 就可以负责这个等待结果出来的⼯作
    • FutureTask 是 Java 并发编程中的一个重要类
    • 它实现了 Future 接口和 Runnable 接口
    • 可以作为 Runnable 被线程执行
    • 可以作为 Future 获取异步计算的结果

举个栗子: 去食堂点餐,前台会给你一张小票,你可以凭借这张小票取餐,而FutureTask就类似于小票的作用。
2. ReentrantLock
ReentrantLock 是 Java 并发包中一个重要的可重入互斥锁,它提供了比 synchronized 更灵活的锁机制。
2.1 ReentrantLock 的⽤法:

  • lock(): 加锁, 如果获取不到锁就死等
  • trylock(超时时间): 加锁, 如果获取不到锁, 等待⼀定的时间之后就放弃加锁
  • unlock(): 解锁
ReentrantLock lock = new ReentrantLock(); 
-----------------------------------------
lock.lock(); 
try { // working 
} finally { lock.unlock() 
}

2.2 ReentrantLock 和 synchronized 的区别

特性synchronizedReentrantLock
实现级别JVM 原生关键字JDK API 类
锁的获取自动获取和释放手动获取和释放
可重入性支持支持
公平性非公平可选择公平/非公平
可中断性不支持支持
超时机制不支持支持
条件变量单个隐式条件多个显式条件
性能优化后很好高竞争下更好
代码灵活性较低较高

选择建议:

  • 优先使用 synchronized:代码简单,自动管理,不易出错
  • 需要高级功能时使用 ReentrantLock:可中断、超时、公平性、多个条件
  • 性能关键且高竞争:考虑 ReentrantLock
  • 代码可维护性:synchronized 更简洁

两者都是线程安全的有效工具,选择取决于具体需求和场景复杂度。

3. 信号量 Semaphore
3.1 什么是信号量?
信号量是一种在并发编程中使用的同步工具,用于控制对共享资源的访问或执行特定操作的线程数量。它维护了一个许可集,线程可以通过获取和释放许可来协调它们的行为。

你可以把它想象成一个管理员,管理着一个有限的停车场。

  • 信号量 = 停车场管理员
  • 许可数 = 停车位的总数
  • 线程 = 想要停车的汽车
  • 获取许可 = 汽车进入停车场(如果车位已满,则必须等待)
  • 释放许可 = 汽车离开停车场(空出一个车位,让等待的汽车进入)

3.2 Java 中的 Semaphore 类

    public static void main(String[] args) {// 4 个线程同时访问, 但是只有 4 个线程能够获取到资源Semaphore semaphore = new Semaphore(4);Runnable runnable = new Runnable() {// 每个线程执行的任务@Overridepublic void run() {try {System.out.println("申请资源");// 申请资源semaphore.acquire();System.out.println("我获取到资源了");Thread.sleep(1000);System.out.println("我释放资源了");// 释放资源semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}};for (int i = 0; i < 20; i++) {Thread t = new Thread(runnable);t.start();}}

4. CountDownLatch
4.1 什么是CountDownLatch?
CountDownLatch 是 Java 并发包中一个非常实用的同步工具类,它允许一个或多个线程等待其他一组线程完成操作。

你可以把它想象成一个倒计时计数器或者跑步比赛的起跑点:

  • 计数器 = 比赛开始前需要做的准备工作数量
  • 裁判 (主线程) = 等待所有准备工作就绪
  • 工作人员 (其他线程) = 执行各项准备工作
  • countDown() = 一项准备工作完成
  • await() = 裁判等待所有准备工作完成

当计数器减到零时,所有等待的线程(裁判)就会被释放,可以继续执行。
4.2 核心机制:
CountDownLatch 使用一个计数器进行初始化,线程通过调用以下两个方法来协调:

  • await():使当前线程等待,直到计数器减到零(除非线程被中断)。
  • countDown():将计数器减 1。如果计数器达到零,则释放所有等待的线程。

重要特性:

  • 计数器无法重置。一旦计数器到达零,它就永远处于触发状态。
  • 这使它特别适合于"一次性"的场景。
    public static void main(String[] args) throws Exception {// 10 个线程等待其他线程执行完毕CountDownLatch latch = new CountDownLatch(10);Runnable r = new Runnable() {// 每个线程执行的任务@Overridepublic void run() {try {Thread.sleep((long) (Math.random() * 10000));// 每个线程执行完毕后, 计数器减 1latch.countDown();} catch (Exception e) {e.printStackTrace();}}};// 10 个线程同时执行任务for (int i = 0; i < 10; i++) {new Thread(r).start();System.out.println("线程" + i + "等待其他线程");}// 等待所有线程执行完毕, 计数器减到 0 时, 继续执行后续代码latch.await();System.out.println("所有线程执行完毕");}
http://www.dtcms.com/a/603327.html

相关文章:

  • win7iis配置网站vps搭建个人网站
  • 【剑斩OFFER】算法的暴力美学——点名
  • 昆明做网站公司我网站建设
  • word文档文字的尾部空格设置不了下划线【解决办法】
  • 网站建设上线多久网站设计公司网页设计
  • 外贸网站一站式海外推广什么软件做网站链接
  • 做货代哪个网站上好找客户想搞一个自己的网站怎么做
  • BitLocker磁盘锁定解决方法
  • jEasyUI 使用标记创建树形菜单
  • 合肥模板网站建设费用帮网站做点击
  • 怎样创建网站dw龙岩平面设计
  • 从脚本到程序:如何构建一个可维护的Python项目结构?
  • 网站建设与维护试题及答案长沙网站建立公司
  • eventfd
  • YOLOv8轻量化改进实战——使模型更适配边缘设备
  • C语言程序编译器 | 提高开发效率,掌握C语言编程的关键工具
  • 泉州 网站建设公司首选东营网站建设培训
  • 南沙网站建设优化微信营销课2013是谁讲的
  • 英语网站排名访问网站速度很慢
  • 3.55基于51单片机温度报警器基于51proteus的DS18B20温度报警器+报告可设置上下限,低于或者高于都会有声光报警,蜂鸣器响LED灯亮。
  • 站牛网是做什么的网站页面如何架构
  • 做自媒体有哪些网站网站建设app开发合同
  • 做外贸搜索外国客户的网站网站建设技术合伙人的技术股份
  • 网站建设公司设计网页的工具甘肃省建设厅官方网站造价总站
  • Unity CullingGroup详解
  • 建设银行曲江支行网站上海信息公司做网站
  • 监理网站广东省公路建设公司网站
  • C语言编译器哪个好用 | 选择适合自己的C语言编译器提升编程效率
  • 企业做宣传网站多少钱制作网页之前必须先建立什么
  • 遗传算法求解TSP旅行商问题python代码实战