多线程

多个线程同时或交替运行, 单核CPU为顺序执行(交替执行), 多核情况下, 每个CPU有自己的运算器, 所以在多个CPU中可以同时运行.

创建线程的方式

1.继承Thread

1
2
3
4
5
6
7
8
public class MyThread extends Thread {

@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class ThreadTest {

public static void main(String[] args) {

MyThread myThread = new MyThread();
myThread.setName("测试");
myThread.start();
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
}

开始线程, 可以看出main线程和测试线程是两个独立的线程
调用myThread.run();方法相当于直接在主线程运行run方法, 而不是开启一个新的线程去执行

2.实现Runnable接口

1
2
3
4
5
6
7
public class MyRunable implements Runnable {

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class ThreadTest {

public static void main(String[] args) {

MyRunable runable = new MyRunable();

Thread thread = new Thread(runable);
thread.start();

System.out.println(Thread.currentThread().getName() + "执行完毕");
}
}

3.使用线程池
3.1 可以在spring中配置相关线程池, 使用时从容器取出即可, 也可以自己声明线程池

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
<bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数,默认为1 -->
<property name="corePoolSize" value="5"/>
<!-- 最大线程数,默认为Integer.MAX_VALUE -->
<property name="maxPoolSize" value="20"/>
<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE
<property name="queueCapacity" value="1000" /> -->

<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="300"/>

<!-- 队列最大长度 -->
<property name="queueCapacity" value="2000"/>

<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
<property name="rejectedExecutionHandler">
<!-- AbortPolicy:直接抛出java.utils.concurrent.RejectedExecutionException异常 -->

<!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->

<!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->

<!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->

<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
</property>

</bean>

3.2 Executors 创建线程池

1
2
3
4
5
6
7
8
9
10
11
12
public class ThreadTest {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {
threadPool.execute(new MyRunable());
}

}
}

当手动创建线程池时, 如果IDEA安装阿里 P3C 插件后会报错提示以下内容, 建议

1
2
3
4
5
6
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。

建议使用如下方式:

1
2
3
4
5
6
7
8
9
10
public class ThreadTest {

public static void main(String[] args) {
// 定时任务 建议为线程起名
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(3,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").build());
executorService.scheduleAtFixedRate(new MyRunable(), 0, 1, TimeUnit.SECONDS);

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ThreadTest {

public static void main(String[] args) {

// 线程工厂
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 20, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown

}
}

线程优先级

1.myThread.setPriority(1);设置优先级
2.优先级从低到高为 1-10, Thread类提供 Thread.MIN_PRIORITY=1, Thread.NORM_PRIORITY=5, Thread.MAX_PRIORITY=10
3.默认优先级为 5 即 NORM_PRIORITY
4.优先级高的仅代表获取进入运行机会的几率大, 并不代表一定会比优先级低的先执行

sleep()和wait()

1.sleep()线程未释放锁, 时间结束后线程继续执行
2.wait线程释放锁, 需要使用notify或notifyAll
3.wait常用于线程之间的交互

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
package com.liuzhihang.tool.alternate;

/**
* 交替打印奇偶数
*
* @author liuzhihang
* @date 2018/9/4 18:39
*/
public class AlternateNum {

public static void main(String[] args) {

Num num = new Num();

Thread thread1 = new Thread(new Odd(num));
Thread thread2 = new Thread(new Even(num));

thread1.start();
thread2.start();

}
}

class Num {
int anInt = 1;
boolean flag = true;

}

class Odd implements Runnable {

private Num num;

public Odd(Num num) {
this.num = num;
}

@Override
public void run() {
while (num.anInt < 1000) {
// 使用同一把锁
synchronized (num) {
if (num.flag) {
System.out.println("奇数 -> " + num.anInt);
num.anInt++;
num.flag = false;
num.notify();

} else {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}

}
}


class Even implements Runnable {

private Num num;

public Even(Num num) {
this.num = num;
}

@Override
public void run() {
while (num.anInt < 1000) {
// 使用同一把锁
synchronized (num) {
if (!num.flag) {
System.out.println("偶数 -> " + num.anInt);
num.anInt++;
num.flag = true;
num.notify();

} else {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}

}
}