![](https://img-blog.csdnimg.cn/20201014180756928.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
多线程
文章平均质量分 68
weixin_46722612
这个作者很懒,什么都没留下…
展开
-
多线程——使用ForkJoin
Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行。我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一个线程内完成:还有一种方法,可以把数组拆成两部分,分别计算,最后加起来就是最终结果,这样可以用两个线程并行执行:如果拆成两部分还是很大,我们还可以继续拆,用4个线程并行执行:这就是Fork/Join任务的原理:判断一个任务是否足够小,如果是,直接计算,否则,就分拆成几个小任务分别计算。这个过程可以反复“裂变”原创 2021-03-18 11:26:54 · 179 阅读 · 0 评论 -
多线程——使用CompletableFuture
使用Future获得异步执行结果时,要么调用阻塞方法get(),要么轮询看isDone()是否为true,这两种方法都不是很好,因为主线程也会被迫等待。从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。我们以获取股票价格为例,看看如何使用CompletableFuture:// CompletableFutureimport java.util.concurrent.Completabl原创 2021-03-18 10:48:08 · 418 阅读 · 0 评论 -
多线程——使用Future
在执行多个任务的时候,使用Java标准库提供的线程池是非常方便的。我们提交的任务只需要实现Runnable接口,就可以让线程池去执行:class Task implements Runnable { public String result; public void run() { this.result = longTimeCalculation(); }}Runnable接口有个问题,它的方法没有返回值。如果任务需要一个返回结果,那么只能保存到变量,还原创 2021-03-18 09:58:46 · 115 阅读 · 0 评论 -
多线程——使用线程池
Java语言虽然内置了多线程支持,启动一个新线程非常方便,但是,创建线程需要操作系统资源(线程资源,栈空间等),频繁创建和销毁大量线程需要消耗大量时间。如果可以复用一组线程:那么我们就可以把很多小任务让一组线程来执行,而不是一个任务对应一个新线程。这种能接收大量小任务并进行分发处理的就是线程池。简单地说,线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待状态。如果有新任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,新任务要么放入队列等待,要么增加一个新线程进行处理。Java标原创 2021-03-18 09:41:04 · 81 阅读 · 0 评论 -
多线程——使用StampedLock
前面介绍的ReadWriteLock可以解决多线程同时读,但只有一个线程能写的问题。如果我们深入分析ReadWriteLock,会发现它有个潜在的问题:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写,这是一种悲观的读锁。要进一步提升并发执行效率,Java 8引入了新的读写锁:StampedLock。StampedLock和ReadWriteLock相比,改进之处在于:读的过程中也允许获取写锁后写入!这样一来,我们读的数据就可能不一致,所以,需要一点额外的代码来判断读的原创 2021-03-18 08:50:02 · 49 阅读 · 0 评论 -
多线程——使用Concurrent集合
我们在前面已经通过ReentrantLock和Condition实现了一个BlockingQueue:public class TaskQueue { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private Queue<String> queue = new LinkedList<>();原创 2021-03-18 08:49:15 · 212 阅读 · 0 评论 -
多线程——使用Atomic
Java的java.util.concurrent包除了提供底层锁、并发集合外,还提供了一组原子操作的封装类,它们位于java.util.concurrent.atomic包。我们以AtomicInteger为例,它提供的主要操作有:增加值并返回新值:int addAndGet(int delta)加1后返回新值:int incrementAndGet()获取当前值:int get()用CAS方式设置:int compareAndSet(int expect, int update)Atomic原创 2021-03-18 08:49:04 · 204 阅读 · 0 评论 -
多线程——使用ReadWriteLock
前面讲到的ReentrantLock保证了只有一个线程可以执行临界区代码:public class Counter { private final Lock lock = new ReentrantLock(); private int[] counts = new int[10]; public void inc(int index) { lock.lock(); try { counts[index] += 1;原创 2021-03-18 08:49:46 · 67 阅读 · 0 评论 -
多线程——使用Condition
使用ReentrantLock比直接使用synchronized更安全,可以替代synchronized进行线程同步。但是,synchronized可以配合wait和notify实现线程在条件不满足时等待,条件满足时唤醒,用ReentrantLock我们怎么编写wait和notify的功能呢?答案是使用Condition对象来实现wait和notify的功能。我们仍然以TaskQueue为例,把前面用synchronized实现的功能通过ReentrantLock和Condition来实现:clas原创 2021-03-18 08:49:34 · 102 阅读 · 0 评论 -
多线程——使用ReentrantLock
从Java 5开始,引入了一个高级的处理并发的java.util.concurrent包,它提供了大量更高级的并发功能,能大大简化多线程程序的编写。我们知道Java语言直接提供了synchronized关键字用于加锁,但这种锁一是很重,二是获取时必须一直等待,没有额外的尝试机制。java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁,我们来看一下传统的synchronized代码:public class Counter {原创 2021-03-17 21:06:21 · 111 阅读 · 0 评论 -
多线程——使用wait和notify
在Java程序中,synchronized解决了多线程竞争的问题。例如,对于一个任务管理器,多个线程同时往队列中添加任务,可以用synchronized加锁:class TaskQueue { Queue<String> queue = new LinkedList<>(); public synchronized void addTask(String s) { this.queue.add(s); }}但是synchronized原创 2021-03-17 20:52:28 · 232 阅读 · 0 评论 -
多线程——死锁
Java的线程锁是可重入的锁。什么是可重入的锁?我们还是来看例子:public class Counter { private int count = 0; public synchronized void add(int n) { if (n < 0) { dec(-n); } else { count += n; } } public synchronized v原创 2021-03-17 19:07:32 · 45 阅读 · 0 评论 -
多线程——同步方法
我们知道Java程序依靠synchronized对线程进行同步,使用synchronized的时候,锁住的是哪个对象非常重要。让线程自己选择锁对象往往会使得代码逻辑混乱,也不利于封装。更好的方法是把synchronized逻辑封装起来。例如,我们编写一个计数器如下:public class Counter { private int count = 0; public void add(int n) { synchronized(this) {原创 2021-03-17 17:27:03 · 58 阅读 · 0 评论 -
多线程——线程同步
当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。这个时候,有个单线程模型下不存在的问题就来了:如果多个线程同时读写共享变量,会出现数据不一致的问题。我们来看一个例子:public class Main { public static void main(String[] args) throws Exception { var add = new AddThread();原创 2021-03-17 17:08:19 · 95 阅读 · 0 评论 -
多线程——守护线程
Java程序入口就是由JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,JVM退出,进程结束。如果有一个线程没有退出,JVM进程就不会退出。所以,必须保证所有线程都能及时结束。但是有一种线程的目的就是无限循环,例如,一个定时触发任务的线程:class TimerThread extends Thread { @Override public void run() { while (true) { System.out原创 2021-03-17 16:39:28 · 53 阅读 · 0 评论 -
多线程——中断线程
如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()方法,使得自身线程能立刻结束运行。我们举个栗子:假设从网络下载一个100M的文件,如果网速很慢,用户等得不耐烦,就可能在下载过程中点“取消”,这时,程序就需要中断下载线程的执行。中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。我们还是看示例代码:publi原创 2021-03-17 16:09:44 · 338 阅读 · 0 评论 -
多线程——线程的状态
在Java程序中,一个线程对象只能调用一次start()方法启动新线程,并在新线程中执行run()方法。一旦run()方法执行完毕,线程就结束了。因此,Java线程的状态有以下几种:New:新创建的线程,尚未执行;Runnable:运行中的线程,正在执行run()方法的Java代码;Blocked:运行中的线程,因为某些操作被阻塞而挂起;Waiting:运行中的线程,因为某些操作在等待中;Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;Terminated:线原创 2021-03-17 10:49:08 · 38 阅读 · 0 评论 -
多线程——创建多线程
Java语言内置了多线程支持。当Java程序启动的时候,实际上是启动了一个JVM进程,然后,JVM启动主线程来执行main()方法。在main()方法中,我们又可以启动其他线程。要创建一个新线程非常容易,我们需要实例化一个Thread实例,然后调用它的start()方法:public class Main { public static void main(String[] args) { Thread t = new Thread(); t.start(); /原创 2021-03-17 09:51:59 · 52 阅读 · 0 评论 -
JAVA笔记——多线程基础
进程在计算机中,我们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类似的,音乐播放器和Word都是进程。某些进程内部还需要同时执行多个子任务。例如,我们在使用Word时,Word可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行打印,我们把子任务称为线程。进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个线程。操作系统调度的最小任务单位其实不是进程,而是线程。常用的Windows、Linux等操作系统都采用抢占式多任务,如何调度线程完全由操作系统决定原创 2021-03-17 08:43:33 · 47 阅读 · 0 评论