线程池

Posted by 余腾 on 2019-07-20
Estimated Reading Time 20 Minutes
Words 4.4k In Total
Viewed Times

一、创建执行线程四种方式

四种方式解答–>这个博客超详细

  • 1、继承 Thread类 创建线程;
  • 2、实现 Runnable接口 创建线程;
  • 3、使用 Callable 和 Future 创建线程;
  • 4、使用线程池例如用 Executor框架;
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
public class UnableToCreateNewNativeThread {
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
thread.start();//Exception in thread "main" java.lang.IllegalThreadStateException
}
}

/*源码层次
public synchronized void start() {
if (threadStatus != 0)//TODO 当一个线程第二次 start 状态已经改变
throw new IllegalThreadStateException();

group.add(this);

boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
*/

Callable

  • Runnable 接口没有返回值,Callable 接口有返回值。

  • Runnable 接口不会抛异常,Callable 接口会抛异常。

  • Runnable -> run,Callable -> call;

  • 一般把 futureTask.get();放在最后面(因为会阻塞)。

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
package Thread.Callable;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableDemo {

public static void main(String[] args) {

//1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
FutureTask<Integer> futureTask = new FutureTask<>(new ThreadDemo());

new Thread(futureTask, "A").start();

//2.接收线程运算后的结果
try {
Integer sum = futureTask.get(); //FutureTask 可用于 闭锁
System.out.println(Thread.currentThread().getName() + "->" + sum);
} catch (Exception e) {
e.printStackTrace();
}
}
}

class ThreadDemo implements Callable<Integer> {

@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 10; i++) {
sum += i;
}
return sum;
}
}

二、ThreadPool 线程池

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入阻塞队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。

  • 主要特点为:线程复用、控制最大并发数、管理线程。
    • 1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    • 2、提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
    • 3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的体系结构

  • java.util.concurrent.Executor :负责线程的使用与调度的根接口
    • ExecutorService 子接口:线程池的主要接口
      • AbstractExecutorService
        • ThreadPoolExecutor 线程池的实现类
      • ScheduledExecutorService 子接口:负责线程的调度
        • ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor

三、Executors

阿里java开发手册 并发处理规定

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

  • 说明:Executors 返回的线程池对象的弊端如下:
    • 1) FixedThreadPool 和 SingleThreadPool:
      允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    • 2) CachedThreadPool:
      允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

JDK 版线程池

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
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {
public static void main(String[] args) {
// System.out.println(Runtime.getRuntime().availableProcessors());//TODO 4线程

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);

//模拟20个线程,请求线程
try {
for (int i = 1; i <= 20; i++) {
cachedThreadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " *");
});
// TimeUnit.MILLISECONDS.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
fixedThreadPool.shutdown();
singleThreadExecutor.shutdown();
cachedThreadPool.shutdown();
}
}
}

手撕线程池

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
import java.util.concurrent.*;

public class ThreadPoolDemo {
public static void main(String[] args) {
// System.out.println(Runtime.getRuntime().availableProcessors());//TODO 4线程

// 7 参
ExecutorService threadPool = new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());

//TODO 请求最大上限为 corePoolSize + maximumPoolSize
//TODO 拒绝策略为 AbortPolicy() java.util.concurrent.RejectedExecutionException
//TODO 拒绝策略为 CallerRunsPolicy() 回退调用者 main 请求
//TODO 拒绝策略为 DiscardPolicy() 直接丢弃
//TODO 拒绝策略为 DiscardOldestPolicy() 丢弃等待最久的任务
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 请求");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}

合理配置线程池线程数

CPU密集型

  • CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU 一直全速运行。

  • CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程)

  • CPU密集型任务配置尽可能少的线程数量;

    • 一般公式:线程池大小 = CPU 核数 + 1个线程

IO密集型

  • 1、由于IO密集型任务线程并不是一直在执行任务(大量的阻塞),则应配置尽可能多的线程,CPU核数 * 2;
  • 2、在单线程上运行I0密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在I0密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻寒时间。
    • I0密集型时,大部分线程都阻塞,故需要多配置线程数;
    • 参考公式:CPU核数/(1 - 阻寨系数) 阻寒系数在0.8~0.9之间;
      • 比如 8核CPU:8/(1 - 0.9) = 80个线程数

Executors.newFixedThreadPool(10)

  • 创建固定数量线程的线程池,推荐执行长期任务。
  • 底层 阻塞队列 LinkedBlockingQueue

  • public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    <!--4-->
    

Executors.newCachedThreadPool()

  • 缓存线程池,线程池的数量不固定。执行很多短期异步,负载较轻的服务。
  • 底层 阻塞队列 SynchronousQueue

  • public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    <!--5-->
  • public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
    }
    <!--6-->
  • int corePoolSize: 线程池中的常驻核心线程数;

  • int maximumPoolSize: 线程池能够容纳同时执行的最大线程数,此值必须大于等于1;

  • long keepAliveTime: 多余的空闲线程的存活时间;

    • 当前线程池数量超过 corePoolsize 时,当空闲时间达到 keepAliveTime 值时,多余空闲线程会被销毁直到只剩下 corePoolSize 个线程为止。
  • TimeUnit unit: keepAliveTime的 单位;

  • BlockingQueue<Runnable> workQueue: 任务队列,被提交但尚未被执行的任务;

  • ThreadFactory threadFactory: 表示生成线程池中工作线程的线程工厂, 用于创建线程,一般用默认的即可。

  • RejectedExecutionHandler handler: 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时,如何拒绝请求执行的runnable的策略。

  • 1、在创建了线程池后,等待提交过来的任务请求。
  • 2、当调用 execute()方法添加一个请求任务时,线程池会做如下判断:
    • 2.1、如果正在运行的线程数量小于corePoolSize、那么马上创建线程运行这个任务;
    • 2.2、如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
    • 2.3、如果这时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    • 2.4、如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
  • 3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
  • 4、当一个线程无事可做超过一定的时间(keepAliveTime) 时,线程池会判断:
    • 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
    • 所以线程池的所有任务完成后它最终会收缩到 corePoolSize 的大小。

线程池拒绝策略

当阻塞队列已经排满了,再也塞不下新任务了同时,线程池中的 maximumPoolSize 线程也达到了最大值,无法继续为新任务服务,此时需要线程池拒绝策略机制合理的处理问题。

1
2
//TODO 线程池第七个参数 handler 均实现了 RejectedExecutionHandler 接口
private static final RejectedExecutionHandler defaultHandle = new AbortPolicy();
  • JDK 内置拒绝策略:
    • AbortPolicy: 直接抛出RejectedExecutionException异常阻止系统正常运行。
    • CallerRunsPolicy: “调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
    • DiscardPolicy: 直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
    • DiscardOldestPolicy: 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。

五、死锁定位

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

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
package Lock;

import java.util.concurrent.TimeUnit;

class HoldLockThread implements Runnable {

private String lockA;
private String lockB;

public HoldLockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}

@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " 自己持有:" + lockA + " 尝试获取:" + lockB);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " 自己持有:" + lockB + " 尝试获取:" + lockA);
}
}
}
}

public class DeadLock {

public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";

new Thread(new HoldLockThread(lockA,lockB),"A").start();
new Thread(new HoldLockThread(lockB,lockA),"B").start();

//linux ps -ef|grep java ls -l
// windows 运行此文件后 jps -l
//jps -l 定位进程号
//jstack xxx 找到原因查看

/*
E:\IntellWorkSpace\LearningNotes\src\Lock>jps -l
11476
14164 Lock.DeadLock
1352 org.jetbrains.jps.cmdline.Launcher
5832 sun.tools.jps.Jps

E:\IntellWorkSpace\LearningNotes\src\Lock>jstack 14164

"B":
at Lock.HoldLockThread.run(DeadLock.java:25)
- waiting to lock <0x00000000d5f45d48> (a java.lang.String)
- locked <0x00000000d5f45d80> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)

"A":
at Lock.HoldLockThread.run(DeadLock.java:25)
- waiting to lock <0x00000000d5f45d80> (a java.lang.String)
- locked <0x00000000d5f45d48> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
*/
}
}

111111111

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
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThreadPool {

public static void main(String[] args) throws Exception {
//1. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);

List<Future<Integer>> list = new ArrayList<>();

for (int i = 0; i < 10; i++) {
Future<Integer> future = pool.submit(new Callable<Integer>(){

@Override
public Integer call() throws Exception {
int sum = 0;

for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
});
list.add(future);
}

pool.shutdown();//一定要关闭

for (Future<Integer> future : list) {
System.out.println(future.get());
}

/*ThreadPoolDemo tpd = new ThreadPoolDemo();

//2. 为线程池中的线程分配任务
for (int i = 0; i < 5; i++) {
pool.submit(tpd);
}

//3. 关闭线程池
pool.shutdown();*/
}

// new Thread(tpd).start(); //频繁创建销毁 耗费资源
// new Thread(tpd).start();

}
class ThreadPoolDemo implements Runnable{

private int i = 0;

@Override
public void run() {
while(i <= 1000){
System.out.println(Thread.currentThread().getName() + " : " + i++);
}
}
}

ScheduledThreadPool 线程池调度

  • ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
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
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TestScheduledThreadPool {

public static void main(String[] args) throws Exception {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);

for (int i = 0; i < 5; i++) {
Future<Integer> result = pool.schedule(new Callable<Integer>(){

@Override
public Integer call() throws Exception {
int num = new Random().nextInt(100);//生成随机数
System.out.println(Thread.currentThread().getName() + " : " + num);
return num;
}

}, 1, TimeUnit.SECONDS);//秒

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

pool.shutdown();//一定要关闭
}
}

ForkJoinPool 分支/合并框架 —>工作窃取 JDK1.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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
import org.junit.Test;

public class TestForkJoinPool {

public static void main(String[] args) {
Instant start = Instant.now();

ForkJoinPool pool = new ForkJoinPool();//需要Pool的支持

ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);

Long sum = pool.invoke(task);

System.out.println(sum);

Instant end = Instant.now();

System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//166-1996-10590
}

@Test
public void test1(){
Instant start = Instant.now();

long sum = 0L;

for (long i = 0L; i <= 50000000000L; i++) {
sum += i;
}

System.out.println(sum);

Instant end = Instant.now();

System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//35-3142-15704
}

//java8 新特性
@Test
public void test2(){
Instant start = Instant.now();

Long sum = LongStream.rangeClosed(0L, 50000000000L)
.parallel()
.reduce(0L, Long::sum);

System.out.println(sum);

Instant end = Instant.now();

System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//1536-8118
}

}
class ForkJoinSumCalculate extends RecursiveTask<Long>{

/**
*
*/
private static final long serialVersionUID = -259195479995561737L;

private long start;
private long end;

private static final long THURSHOLD = 10000L; //临界值

public ForkJoinSumCalculate(long start, long end) {
this.start = start;
this.end = end;
}

@Override
protected Long compute() {
long length = end - start;

if(length <= THURSHOLD){
long sum = 0L;

for (long i = start; i <= end; i++) {
sum += i;
}

return sum;
}else{
long middle = (start + end) / 2;

ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
left.fork(); //进行拆分,同时压入线程队列

ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);
right.fork(); //

return left.join() + right.join();
}
}
}

生产者和消费者案例 synchronized

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
public class TestProductorAndConsumer {

public static void main(String[] args) {
Clerk clerk = new Clerk();

Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);

// 如果不用等待唤醒机制会产生 丢失数据
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();

new Thread(pro, "生产者 C").start();
new Thread(cus, "消费者 D").start();
}

}

//店员
class Clerk {
private int product = 0;

// 进货
public synchronized void get() {// 循环次数:0
//如果用if 会造成虚假唤醒
while(product >= 1) {// 为了避免虚假唤醒问题,应该总是使用在循环中
System.out.println("产品已满!");

try {
this.wait();
} catch (InterruptedException e) {
}
}
//以下代码如果放进else 有可能 无线程唤醒
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}

// 卖货
public synchronized void sale() {// product = 0; 循环次数:0
//如果用if 会造成虚假唤醒
while (product <= 0) {
System.out.println("缺货!");

try {
this.wait();// 在这里等待,获得锁之后,在这个地方往下执行。
} catch (InterruptedException e) {
}
}
//以下代码如果放进else 有可能 无线程唤醒
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();

}
}

//生产者
class Productor implements Runnable {
private Clerk clerk;

public Productor(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
// 产生延迟 有问题
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}

clerk.get();
}
}
}

//消费者
class Consumer implements Runnable {
private Clerk clerk;

public Consumer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}

生产者和消费者案例 同步锁Lock

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
103
104
105
106
107
108
109
110
111
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestProductorAndConsumerForLock {

public static void main(String[] args) {
Clerk clerk = new Clerk();

Productor pro = new Productor(clerk);
Consumer con = new Consumer(clerk);

new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start();

new Thread(pro, "生产者 C").start();
new Thread(con, "消费者 D").start();
}

}

class Clerk {
private int product = 0;

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

// 进货
public void get() {
lock.lock();

try {
while (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!");

try {
condition.await();
} catch (InterruptedException e) {
}

}
System.out.println(Thread.currentThread().getName() + " : " + ++product);

condition.signalAll();
} finally {
//一定放在finally 一定要释放
lock.unlock();
}

}
// 卖货
public void sale() {
lock.lock();

try {
while (product <= 0) {
System.out.println("缺货!");

try {
condition.await();
} catch (InterruptedException e) {
}
}

System.out.println(Thread.currentThread().getName() + " : " + --product);

condition.signalAll();

} finally {
lock.unlock();
}
}
}
// 生产者
class Productor implements Runnable {

private Clerk clerk;

public Productor(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {

private Clerk clerk;

public Consumer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}

Thread8Monitor 线程八锁

题目:判断打印的 “one” or “two” ?

  1. 两个普通同步方法,两个线程,标准打印, 打印? //one two

  2. 新增 Thread.sleep() 给 getOne() ,打印? //one two

  3. 新增普通方法 getThree() , 打印? //three one two getOne()不是静态

  4. 两个普通同步方法,两个 Number 对象,打印? //two one

  5. 修改 getOne() 为静态同步方法,打印? //two one

  6. 修改两个方法均为静态同步方法,一个 Number 对象? //one two

  7. 一个静态同步方法,一个非静态同步方法,两个 Number 对象? //two one

  8. 两个静态同步方法,两个 Number 对象? //one two

    线程八锁的关键:
    ①非静态方法的锁默认为 this, 静态方法的锁为 对应的 Class 实例
    ②某一个时刻内,只能有一个线程持有锁,无论几个方法。


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
public class TestThread8Monitor {

public static void main(String[] args) {

Number number = new Number();
Number number2 = new Number(); // 4锁

new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
// number.getTwo(); //1、普通同步方法 2、加sleep 3、新增方法 getThree() --> 方法getOne 均无静态 static
//5、 getOne()加静态

number2.getTwo(); //4锁
}
}).start();

/*new Thread(new Runnable() {
@Override
public void run() {
number.getThree();
}
}).start();*/
}

}
class Number{

public static synchronized void getOne(){//静态方法的锁为 Number.class
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}

System.out.println("one");
}

public /*static*/ synchronized void getTwo(){//非静态方法的锁为this
System.out.println("two");
}

public void getThree(){
System.out.println("three");
}
}

感谢阅读


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !