JUC并发编程系列(三)

11.线程池

线程池:三大方法、七大参数、四种拒绝策略

池化技术

程序的运行,本质:占用系统的资源! 优化资源的使用!=>池化技术

线程池、连接池、内存池、对象池///….. 创建、销毁。十分浪费资源

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处:

1.降低资源的消耗

2.提高响应的速度

3.方便管理

使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者 “过度切换”的问题。

线程池:三大方法

其中Integer.MAX_VALUE约为21亿,FixedThreadPool是伸缩性的线程池,最小为1,最大为21亿,可想若创建21亿个线程,大量请求堆积,势必会造成内存溢出。创建一个线程的线程池又不能最大程度利用机器性能,所以阿里开发手册规定需要手动创建线程池!

下面我们先使用Executors来创建线程池,分析三种创建的底层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 线程池
* 三大方法、7大参数、4种拒绝策略
*
* @author 路飞
* @create 2021/1/19
*/
public class Demo01 {
public static void main(String[] args) {
//阿里开发手册规定:不允许使用Executors创建线程池,而是通过ThreadPoolExcutor的方式常见,
//明确写出创建的线程测的规则,避免资源耗尽的风险
//工具类Executors创建的三种线程池底层均是ThreadPoolExecutor
//故手写线程池,只需要通过ThreadPoolExecutor 创建即可
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定线程池大小
//ExecutorService threadPool = Executors.newCachedThreadPool(); //可伸缩线程池 遇强则强,遇弱则弱

//使用线程池操作线程
try {
for (int i = 0; i < 30; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " OK");
});
}

} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool.shutdown();
}
}
}

Executors.newSingleThreadExecutor();

底层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

可以看到是通过ThreadPoolExecutor()的有参构造创建的,具体每个变量是什么意思呢?

线程池:七大参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

到这,初中生都能看懂的英语,依次是线程池的核心大小,最大线程数,存活时间,时间单位,底层实现的队列,默认线程池构造工厂,默认拒绝策略,这就是线程池的七大参数。

参数中大部分我们都知道填什么,就最后这个handler是什么?点进去发现是个接口,那就看实现类

可以看到这个接口有四个实现类,查看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
//用于拒绝任务的处理程序,可以直接在{@code execute}方法的调用线程中运
//行被拒绝的任务*除非执行器已被关闭,否则这种情况下该任务将被丢弃。
//简单点就是谁调用谁执行
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }

/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
//直接抛异常
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }

/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}

/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
//直接放弃
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }

/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}

/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
//放弃最旧未处理的程序
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }

/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}

new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常

new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!

new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!

new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!

可以画一个图,具体理解7大参数的意思

手写线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* 手动创建线程池
*
* @author 路飞
* @create 2021/1/19
*/
public class Demo02 {
public static void main(String[] args) {
// 最大线程到底该如何定义
// 1、CPU 密集型,几核,就是几*2,可以保持CPu的效率最高!
// 2、IO 密集型 > 判断你程序中十分耗IO的线程,
// 程序 15个大型任务 io十分占用资源!

System.out.println(Runtime.getRuntime().availableProcessors());
//ThreadPoolExecutor的构造方法
/**
* 七大参数
* public ThreadPoolExecutor(int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory) {
* this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
* threadFactory, defaultHandler);
* }
*
*
* 四种拒绝策略
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}. 不处理,抛异常
* AbortPolicy
*
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded. 哪来的去哪里!
* 用于拒绝任务的处理程序,可以直接在方法的调用线程中运行被拒绝的任务*除非执行器已被关闭,否则这种情况下该任务将被丢弃。
* CallerRunsPolicy
*
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
* 拒绝任务的处理程序,它丢弃<p>最旧</p>的未处理*请求,然后重试,除非执行程序*被关闭,在这种情况下,该任务将被丢弃。
* DiscardOldestPolicy
*
* A handler for rejected tasks that silently discards the
* rejected task. 直接抛弃
* DiscardPolicy
*
*
*
*
*
*/
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), //阻塞队列实现
Executors.defaultThreadFactory(), //默认线程工厂
new ThreadPoolExecutor.DiscardPolicy() //拒绝策略
);

/**
* 最大承载:max+deque
* 超过RejectedExecutionException
*/


try {
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
}

线程池的大小如何设置?

IO密集型,CPU密集型:(调优)

通过Runtime.getRuntime().availableProcessors()拿到机器的逻辑处理器数量,即为线程池最大线程数

12.四大函数式接口

函数式接口:只有一个方法的接口

目的:提供了基础编程模型,简化编程

1
2
3
4
5
6
7
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//泛型、枚举、反射
//lambda表达式,链式编程,函数式接口,Stream流式计算
//FunctionalInterface

Function函数式接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Represents a function that accepts one argument and produces a result.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
*
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {

/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);

T为传入参数的类型,R为返回参数的类型

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Function 函数型接口,有一个输入参数,一个输出
* 只要是函数型接口 可以用lambda表达简化
* @author 路飞
* @create 2021/1/20
*/
public class Demo1 {
public static void main(String[] args) {

// Function function = new Function<String,String>() {
// @Override
// public String apply(String str) {
// return str;
// }
// };

//使用lambda简化
Function function = (str)->{return str;};
System.out.println(function.apply("1223"));

}
}

断定型接口:有一个输入参数,返回值只能是布尔值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {

/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 断定型接口:有一个输入参数,返回值只能是一个 boolean
* @author 路飞
* @create 2021/1/20
*/
public class Demo2 {
public static void main(String[] args) {
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String str) {
// return str.isEmpty();
// }
// };

Predicate<String> predicate = (str)->{return str.isEmpty();};
System.out.println(predicate.test("a"));

}
}

Consumer消费性接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);

只有输入,无返回值

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Consumer 消费型接口:只要输入,无返回值
* @author 路飞
* @create 2021/1/20
*/
public class Demo3 {
public static void main(String[] args) {
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// };
// consumer.accept("aaaa");

Consumer<String> consumer =(s)->{
System.out.println(s);
};
consumer.accept("aaa");
}
}

Supplier 供给型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Represents a supplier of results.
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {

/**
* Gets a result.
*
* @return a result
*/
T get();
}

无参数,只有返回值

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Supplier 供给型接口:没有参数,只有返回
* @author 路飞
* @create 2021/1/20
*/
public class Demo4 {
public static void main(String[] args) {
// Supplier<Integer> supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// return 1024;
// }
// };

Supplier<Integer> supplier = ()->{return 1024;};

System.out.println(supplier.get());
}
}

13.Stream流式计算

什么是Stream?

这里说的Stream不是IO里的输入输出流,Stream流是一个集合元素的函数模型(处理的步骤方法),它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 题目要求:一分钟内完成此题,只能用一行代码实现!
* 现在有5个用户!筛选:
* 1、ID 必须是偶数
* 2、年龄必须大于23岁
* 3、用户名转为大写字母
* 4、用户名字母倒着排序
* 5、只输出一个用户!
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(6,"e",25);

//转集合 方便操作
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
//stream
list.stream()
.filter((u)->{return u.getId()%2==0;})
.filter((u)->{return u.getAge()>23;})
.map((u)->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1).forEach(System.out::println);
}
}

14.ForkJoin

什么是ForkJoin

ForkJoin在JDK1.7,并行执行任务!提高效率!

查看JDK1.8帮助文档:

可以看到,这是类似一个分任务递归的去执行,根据需求可以选择无返回值和有返回值的方法

可以写个测试例子,对比三种求和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* ForkJoin分支合并
* 使用方法:
* 1.forkJoinPool 通过它执行
* 2.计算任务 forkjoinPool.execute(ForkJoinTask task)
* 3.计算类要继承 ForkJoinTask
* 求和任务 计算1-10亿的和
*
* @author 路飞
* @create 2021/1/20
*/
public class ForkJoinDemo extends RecursiveTask<Long> {

private Long start;
private Long end;

//临界值
private Long temp = 10000L;

public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}



@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else { //forkjoin递归
long middle = (start + end) / 2;
ForkJoinDemo task1 = new ForkJoinDemo(start , middle);
task1.fork(); // 拆分任务,把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1 , end);
task2.fork(); // 拆分任务,把任务压入线程队列
return task1.join() + task2.join();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 测试3种求和方式的性能
* @author 路飞
* @create 2021/1/20
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1(); //sum=500000000500000000 花费时间:8366
//test2(); // sum=500000000500000000 花费时间:8599
test3(); //sum=500000000500000000 花费时间:238
}

//1.直接for循环求
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum +=i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 花费时间:"+(end-start));
}

//2.forJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 花费时间:"+(end-start));
}

//3.Stream并行流操作
public static void test3(){
long start = System.currentTimeMillis();
//Stream 并行流
Long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0,Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 花费时间:"+(end-start));
}
}

可以看到Stream的计算巨快!

这里也只是讲解下ForkJoin的思想,Stream的相关的API在JDK帮助文档中写的很清楚,当遇到某个需求时,可以直接去查阅即可,这里就不过多阐述。

15.异步回调

Future设计的初衷:对将来的某个事件的结果进行建模

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 异步调用:CompletableFuture
* 类似Ajax Success error
* 异步执行
* 成功回调
* 失败回调
* @author 路飞
* @create 2021/1/20
*/
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {

//无返回值的runAsync 异步回调
// CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
// });
// System.out.println("111");
// completableFuture.get(); //获取回调结果

//有返回值的回调

CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
int i = 10 / 0;
return 1024;
});

completableFuture.whenComplete((t,u)->{
System.out.println("t====>"+t); //正确的返回结果
System.out.println("u====>"+u); //错误的返回结果
}).exceptionally((e)->{ //错误的返回函数
System.out.println(e.getMessage()); //CompletionException
return 233;
}).get();
}
}

16.JMM

Volatile的理解

Volatile 是 Java 虚拟机提供轻量级的同步机制

1、保证可见性

2、不保证原子性

3、禁止指令重排

JMM是什么?

Java的并发采用的是共享内存模型

关于JMM的一些同步约定:

1.线程解锁前,必须把共享变量立刻刷回主存;

2.线程加锁前,必须读取主存中的最新值到工作内存中;

3.加锁和解锁是同一把锁。

什么是工作内存?什么是主存?

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类

型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write

  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存

  • 不允许一个线程将没有assign的数据从工作内存同步回主内存

  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过assign和load操作

  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量对一个变量进行unlock操作之前,必须把此变量同步回主内存

写个测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Volatile的可见性
* @author 路飞
* @create 2021/1/20
*/
public class JMMDemo1 {
private volatile static int num = 0;

//主存中num已经置为10 但线程中未得到信息 陷入死循环
//加入Volatile 保证可见性
public static void main(String[] args) {

new Thread(()->{
while (num == 0){

}
}).start();

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}

num = 10;
System.out.println("num:"+num);

}
}

main线程中把num置为10了,但新开的线程由于不知道到把num置为10,一直在while循环中跳不出来,程序出现死循环,解决的办法就是在num的声明中加入volatile,保证线程之间的可见性!

关于volatile具体的理解和测试,将在下一篇文章进行详细讲解。