|
@@ -12,14 +12,14 @@ wait()和notify()必须放在synchronized块中是因为这些方法依赖于对
|
|
|
|
|
|
## volatile 关键字
|
|
|
|
|
|
-在Java中,volatile关键字是用于保证多线程环境下变量的可见性和有序性。主要分为两大功能。
|
|
|
+在Java中,volatile关键字是用于保证多线程环境下变量的可见性和有序性。
|
|
|
|
|
|
1. 保证变量可见性:被volatile关键字声明代表变量是共享且不稳定的,每次使用它都到主存中进行读取,并且强制刷新到内存。
|
|
|
-2. 进制指令重排序:JVM 具有指令重排的特性,可以保证程序执行效率更高,但是volatile关键字会进制指令重排保证其有序性。
|
|
|
+2. 保证变量有序性:JVM 具有指令重排的特性,可以保证程序执行效率更高,但是volatile关键字会进制指令重排保证其有序性。
|
|
|
|
|
|
## volatile 关键字底层原理
|
|
|
|
|
|
-`volatile`是通过编译器在生成字节码时,在指令序列中添加“**内存屏障**”来禁止指令重排序的。
|
|
|
+`volatile`是通过编译器在生成字节码时,在指令序列中添加“**内存屏障**”来保证变量可见性。
|
|
|
|
|
|
JMM层面的“**内存屏障**”:
|
|
|
|
|
@@ -57,7 +57,7 @@ JVM的实现会在volatile读写前后均加上内存屏障,在一定程度上
|
|
|
|
|
|
轻量级之所以是轻量级锁,是因为它仅仅使用 CAS 进行操作来获取锁。如果获取成功那么会直接获取锁,如果失败,当前线程便尝试使用自旋来获取锁。当竞争线程的自旋次数达到界限值(`threshold`),轻量级锁将会膨胀为重量级锁。
|
|
|
|
|
|
-重量级锁(`heavy weight lock`),是使用操作系统互斥量(`mutex`)来实现的传统锁。 当所有对锁的优化都失效时,将退回到重量级锁。它与轻量级锁不同竞争的线程不再通过自旋来竞争线程, 而是直接进入堵塞状态,此时不消耗CPU,然后等拥有锁的线程释放锁后,唤醒堵塞的线程, 然后线程再次竞争锁。但是注意,当锁膨胀(`inflate`)为重量锁时,就不能再退回到轻量级锁。
|
|
|
+重量级锁,是使用操作系统互斥量(`mutex`)来实现的传统锁。 当所有对锁的优化都失效时,将退回到重量级锁。它与轻量级锁不同竞争的线程不再通过自旋来竞争线程, 而是直接进入堵塞状态,此时不消耗CPU,然后等拥有锁的线程释放锁后,唤醒堵塞的线程, 然后线程再次竞争锁。但是注意,当锁膨胀为重量锁时,就不能再退回到轻量级锁。
|
|
|
|
|
|
## synchronized 和 volatile 有什么区别?
|
|
|
|
|
@@ -91,7 +91,7 @@ AQS提供了一种通用的框架,用于实现线程间的协作和同步操
|
|
|
|
|
|
## synchronized 和 ReentrantLock 有什么区别?
|
|
|
|
|
|
-* synchronized 依赖于 JVM 而 `ReentrantLock` 依赖于 API
|
|
|
+* `synchronized` 依赖于 JVM 而 `ReentrantLock` 依赖于 API
|
|
|
* `ReentrantLock`可以指定是公平锁还是非公平锁。而`synchronized`只能是非公平锁。
|
|
|
* `synchronized`是以代码块形式实现的,因此只能对整个代码块进行同步操作,无法在代码块内部实现一些细粒度的控制。而`ReentrantLock`可以通过`Condition`对象实现线程间的协作和控制。
|
|
|
|
|
@@ -168,6 +168,69 @@ AQS提供了一种通用的框架,用于实现线程间的协作和同步操
|
|
|
|
|
|
CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。
|
|
|
|
|
|
+## 线程池,如果使用future,则必须重构拒绝策略
|
|
|
+
|
|
|
+* Future类的get方法
|
|
|
+
|
|
|
+```java
|
|
|
+private static final int NEW = 0;
|
|
|
+private static final int COMPLETING = 1;
|
|
|
+private static final int NORMAL = 2;
|
|
|
+private static final int EXCEPTIONAL = 3;
|
|
|
+private static final int CANCELLED = 4;
|
|
|
+private static final int INTERRUPTING = 5;
|
|
|
+private static final int INTERRUPTED = 6;
|
|
|
+
|
|
|
+public V get() throws InterruptedException, ExecutionException {
|
|
|
+ int s = state;
|
|
|
+ // 初始状态默认为0
|
|
|
+ if (s <= COMPLETING)
|
|
|
+ // 如果小于COMPLETING则阻塞
|
|
|
+ s = awaitDone(false, 0L);
|
|
|
+ return report(s);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+如果没有调用run方法改变线程状态则future则会被一直阻塞导致OOM
|
|
|
+
|
|
|
+```java
|
|
|
+public void run() {
|
|
|
+ if (state != NEW ||
|
|
|
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
|
|
|
+ null, Thread.currentThread()))
|
|
|
+ return;
|
|
|
+ try {
|
|
|
+ Callable<V> c = callable;
|
|
|
+ if (c != null && state == NEW) {
|
|
|
+ V result;
|
|
|
+ boolean ran;
|
|
|
+ try {
|
|
|
+ result = c.call();
|
|
|
+ ran = true;
|
|
|
+ } catch (Throwable ex) {
|
|
|
+ result = null;
|
|
|
+ ran = false;
|
|
|
+ setException(ex);
|
|
|
+ }
|
|
|
+ if (ran)
|
|
|
+ // 改变状态
|
|
|
+ set(result);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ // runner must be non-null until state is settled to
|
|
|
+ // prevent concurrent calls to run()
|
|
|
+ runner = null;
|
|
|
+ // state must be re-read after nulling runner to prevent
|
|
|
+ // leaked interrupts
|
|
|
+ int s = state;
|
|
|
+ if (s >= INTERRUPTING)
|
|
|
+ handlePossibleCancellationInterrupt(s);
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
## ThreadLocal
|
|
|
|
|
|
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的,想要每一个线程都有自己私有的本地变量就就需要使用ThreadLocal类,**`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。**
|