本人详解
作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》
公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题
中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯
转载说明:务必注明来源(注明:作者:王文峰哦)
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站
JAVA异步编程并发编程见解【CompletableFuture】【 Fork/Join】【内置锁】【显式锁】
学习教程(传送门)
1、掌握 JAVA入门到进阶知识(持续写作中……)
2、学会Oracle数据库用法(创作中……)
3、手把手教你vbs脚本制作(完善中……)
4、牛逼哄哄的 IDEA编程利器(编写中……)
5、吐血整理的 面试技巧(更新中……)
Java 并发编程异步编程并发编程见解【CompletableFuture】【 Fork/Join】【内置锁】【显式锁】
目录
简介
并发编程是Java中一个非常重要的主题,它允许程序同时执行多个任务,从而提高效率。然而,并发编程也带来了复杂性,例如竞态条件、死锁等问题。本文将介绍Java并发编程的基础知识以及一些高级特性。
线程基础
创建线程
在Java中,可以通过继承Thread
类或实现Runnable
接口来创建线程。自Java 8以来,使用lambda
表达式可以使代码更加简洁。
// 继承 Thread 类
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
}
// 实现 Runnable 接口
Runnable task = () -> System.out.println("Task is running");
// 使用 lambda 表达式
new Thread(() -> System.out.println("Lambda thread is running")).start();
线程状态
线程在其生命周期中有几种不同的状态:新建(New)、可运行(Runnable)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed Waiting)和终止(Terminated)。
同步与锁
内置锁
Java提供了内置锁机制,即synchronized
关键字,用于确保同一时刻只有一个线程可以执行被该关键字修饰的方法或代码块。
public synchronized void method1() {
// 只有一个线程可以访问此方法
}
public void method2() {
synchronized (this) {
// 只有一个线程可以访问此代码块
}
}
显式锁
除了内置锁,Java还提供了ReentrantLock
等显式锁,它们提供了比内置锁更灵活的锁定协议。
Lock lock = new ReentrantLock();
lock.lock();
try {
// 需要同步的代码
} finally {
lock.unlock();
}
并发集合
Java提供了一系列线程安全的集合类,如ConcurrentHashMap
、CopyOnWriteArrayList
等,这些集合可以在多线程环境中高效地工作。
原子变量
Java.util.concurrent.atomic包提供了几个原子操作类,比如AtomicInteger
、AtomicLong
等,它们可以在不使用锁的情况下实现线程安全的操作。
线程池
线程池通过重用预先创建的线程来减少线程创建和销毁的开销。Executors
工厂类可以帮助我们快速创建不同类型的线程池。
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> System.out.println("Task submitted to thread pool"));
executor.shutdown();
Fork/Join 框架
Fork/Join框架是一种用于并行执行任务的框架,特别适用于可以分解为多个子任务的计算密集型任务。
class Fibonacci extends RecursiveTask<Integer> {
final int n;
Fibonacci(int n) { this.n = n; }
protected Integer compute() {
if (n <= 1)
return n;
Fibonacci f1 = new Fibonacci(n - 1);
f1.fork();
Fibonacci f2 = new Fibonacci(n - 2);
return f2.compute() + f1.join();
}
}
CompletableFuture
CompletableFuture
是在Java 8引入的一个强大的异步编程工具,它可以用来组合异步操作。
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(String::toUpperCase)
.thenAccept(System.out::println);
最佳实践
- 避免不必要的同步:只对需要保护的代码段进行同步。
- 优先使用并发集合:当可能有多个线程访问时,使用并发集合而不是同步包装器。
- 使用线程池:不要直接创建线程,而是使用线程池来管理线程。
- 选择合适的锁机制:根据具体情况选择内置锁或显式锁。
常见问题
在深度学习的并发编程场景中,也可能会出现死锁、活锁和饥饿等问题,以下是针对这三个问题的详细分析:
死锁
-
定义:两个或更多线程互相等待对方持有的锁,导致所有线程都无法继续执行。
-
产生原因:
- 系统资源竞争:系统中拥有的多个不可剥夺资源数量不足以满足多个线程运行的需要,导致线程在运行过程中因为争夺资源而陷入僵局。
- 进程推进顺序非法:线程请求和释放资源的顺序不当,也可能导致死锁。
-
必要条件:
- 互斥条件:线程对所分配到的资源进行排他性使用,即在一段时间内某资源只由一个线程占用。
- 请求和保持条件:线程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程阻塞,但又对自己已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 环路等待条件:在发生死锁时,必然存在一个线程-资源的环形链。
-
解决方案:
- 避免嵌套锁:尝试最小化每个线程中锁的数量,减少获取多个锁的场景。
- 锁排序:为应用中的锁定行为指定一个严格的顺序,然后总是按这个顺序获得锁。
- 使用定时锁:使用tryLock方法尝试获取锁,它允许线程等待锁一段时间,如果无法获取,线程可以放弃并做其他事情。
活锁
-
定义:线程频繁尝试执行某些操作,但总是因为竞争而失败,导致线程无法取得进展。活锁的状态是变化的,只是无法达到目的。
-
产生原因:最常见的原因是线程组的执行顺序不合理,导致某些先需要的资源被后置。此外,资源分配的随机选择也可能加剧活锁问题。
-
与死锁的区别:活锁有可能在一定时间后自动解开,但死锁不能。在死锁场景中,所有相关线程实际上被冻结,无法继续执行。
-
解决方案:
- 增加随机性:在重试机制中引入随机性,比如随机等待一段时间后再尝试,这有助于打破对称性。
- 减少依赖:尽量减少线程间的相互依赖,让线程能够尽可能独立地完成任务,减少因依赖产生的等待。
饥饿
-
定义:某个线程由于其他线程的竞争而永远得不到所需的资源。
-
产生原因:通常是由于线程优先级导致的。高优先级的线程持续抢占所有的资源,而低优先级线程则得不到执行。
-
影响:导致低优先级任务缺乏执行所需的资源,造成低效率和潜在的系统瓶颈。被饥饿线程经历的长期等待时间可能严重影响系统的整体响应能力。
-
解决方案:
- 使用公平锁:在锁的实现中使用公平策略,确保每个线程都能获得执行机会。
- 调整线程优先级:适当调整线程的优先级,避免优先级过高的线程长时间霸占资源。
- 实施公平的调度算法:如时间片轮转和公平共享调度,以防止任何单一线程垄断资源。
总结
Java并发编程虽然强大,但也充满了挑战。正确理解和应用并发编程的原则和技术,可以显著提升应用程序的性能和响应能力。希望这篇文章能够帮助你更好地掌握Java并发编程的核心概念和技术。
学习教程(传送门)
1、掌握 JAVA入门到进阶知识(持续写作中……)
2、学会Oracle数据库用法(创作中……)
3、手把手教你vbs脚本制作(完善中……)
4、牛逼哄哄的 IDEA编程利器(编写中……)
5、吐血整理的 面试技巧(更新中……)
往期文章
第一章:日常_JAVA_面试题集15(含答案)
第二章:日常_JAVA_面试题集14(含答案)
平安壹钱包面试官:请你说一下Mybatis的实现原理
Java开发-热点-热门问题精华核心总结-推荐
往期文章大全……
一键三连 一键三连 一键三连~
本人详解
作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》
公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题
中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯
转载说明:务必注明来源(注明:作者:王文峰哦)
一键三连 一键三连 一键三连~
以上就是今天的内容,关注我,不迷路