一个示例回顾 Future
在一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度。
JDK5 新增了Future
接口,用于描述一个异步计算的结果。
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()
的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone
任务是否结束,再获取结果。
在一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度。
JDK5 新增了Future
接口,用于描述一个异步计算的结果。
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()
的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone
任务是否结束,再获取结果。
前面我们在讲 CAS 和原子操作 atomic 类的时候,都讲到了 Unsafe。
Unsafe 是 Java 中一个非常特殊的类,它为 Java 提供了一种底层、"不安全"的机制来直接访问和操作内存、线程和对象。正如其名字所暗示的,Unsafe 提供了许多不安全的操作,因此它的使用应该非常小心,并限于那些确实需要使用这些底层操作的场景。
在第一节:初步掌握 Java 多线程中,我们讲述了创建线程的 3 种方式,一种是直接继承 Thread,一种是实现 Runnable 接口,另外一种是实现 Callable 接口。
前 2 种方式都有一个缺陷:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者线程通信的方式来达到目的,这样使用起来就比较麻烦。
Java 1.5 提供了 Callable、Future、FutureTask,它们可以在任务执行完后得到执行结果,今天我们就来详细的了解一下。
在 JDK 1.5 之前,面对 Java 并发问题, synchronized 是一招鲜的解决方案:
拿同步块来举例:
public void test(){
synchronized (object) {
i++;
}
}
Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8和Netty 3.10.6)、使用场景进行举例,为读者介绍主流锁的知识点,以及不同的锁的适用场景。
Java中往往是按照是否含有某一特性来定义锁,我们通过特性将锁进行分组归类,再使用对比的方式进行介绍,帮助大家更快捷的理解相关知识。下面给出本文内容的总体分类目录:
“三妹,今天我们来学习 synchronized 关键字吧。”我瞅了一眼坐在沙发上的三妹说,她正在王者荣耀呢。
三妹(颜值在线,气质也在线)喝了一口冰可乐接着说:“好的,二哥。马上,打完这把,看我三杀。”
在 Java 中,关键字 synchronized 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到 synchronized 的另外一个重要的作用,synchronized 可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代 volatile 功能)。
BlockingQueue 是 Java 中的一个接口,它代表了一个线程安全的队列,不仅可以由多个线程并发访问,还添加了等待/通知机制,以便在队列为空时阻塞获取元素的线程,直到队列变得可用,或者在队列满时阻塞插入元素的线程,直到队列变得可用。
最常见的"生产者-消费者"问题中,队列通常被视作线程间的数据容器,生产者将“生产”出来的数据放入数据容器,消费者从“数据容器”中获取数据,这样,生产者线程和消费者线程就解耦了,各自只需要专注自己的业务即可。
ConcurrentHashMap 是 Java 并发包 (java.util.concurrent) 中的一种线程安全的哈希表实现。
HashMap 在多线程环境下扩容会出现 CPU 接近 100% 的情况,因为 HashMap 并不是线程安全的,我们可以通过 Collections 的Map<K,V> synchronizedMap(Map<K,V> m)
将 HashMap 包装成一个线程安全的 map。
ConcurrentLinkedQueue 是 java.util.concurrent
(JUC) 包下的一个线程安全的队列实现。基于非阻塞算法(Michael-Scott 非阻塞算法的一种变体),这意味着 ConcurrentLinkedQueue 不再使用传统的锁机制来保护数据安全,而是依靠底层原子的操作(如 CAS)来实现。
Michael-Scott 由 Maged M. Michael 和 Michael L. Scott 在 1996 年提出,在这种算法中,一个线程的失败或挂起不会导致其他线程也失败或挂起。
学过 ArrayList 的小伙伴应该记得,ArrayList 是一个线程不安全的容器,如果在多线程环境下使用,需要手动加锁,或者使用 Collections.synchronizedList()
方法将其转换为线程安全的容器。
否则,将会出现 ConcurrentModificationException 异常。