多线程

多线程

程序:程序是一段指令集和,是静态的。

进程:当一个程序正在执行时,对应一个进程。

​ 进程由程序、数据和PCB(Process Control Block)组成,是操作系统 资源分配和调度的独立单位

线程:为了进一步改善资源利用率、提高系统运行效率而引入的机制。 在一个进程中,多个并行的线程分别执行不同的任务,能更有效的利 用系统资源,提高程序运行效率。

==线程==是进程中的一个相对独立的指令序列,它也可被操作系统独立调 度。

==多线程==就是指一个进程中有多个正在运行中的且可被独立调度的指令 序列

线程和进程都拥有对应的执行控制结构。但==线程是从属于某个进程的, 它只能使用所属进程的内存资源==。

==进程内部的多个线程之间共享该进程的地址空间和资源==。 ==每个线程有独立的线程栈==。

线程和进程的关系: 一个进程可以包含多个线程,而一个线程只能属于某一个进程,线程不能 脱离进程而独立运行;

每一个进程至少包含一个线程(称为主线程);

java程序的入口main()方法就 是在主线程中被执行的。 在主线程中可以创建并启动其它的线程;

一个进程内的所有线程共享该进程的内存资源。

创建多线程的方式:

  • 继承Thread类:

    创建Thread类的子类,在子类中重写run()方法。

    可使用Thread类的构造器Thread(String name)为新线程指定名称。

    调用start()方法启动新线程。

    java.lang.Thread类封装了“线程”的功能。

    Thread类的实例代表一个线程对象,借助该实例可以启动和管理一个线 程。

    ==Thread类的构造函数==

    public Thread()

    public Thread(String name) //参数name代表线程名

    public Thread(Runnable target) //参数target为可运行的对象

    ==Thread类中的方法:==

    public void start() //启动线程

    public void run() //线程的进入点。通过定义Thread类的子类,并重写 run()方法以提供子线程要执行的功能。

    public final String getName() //线程名

    ==Thread类中的两个静态方法==

    public static void sleep(long millis) //使得当前线程休眠指定的时间

    public static Thread currentThread() //返回当前线程对象

  • 实现Runnable接口创建线程:

    Thread类的构造函数:

    public Thread(Runnable target)

    public Thread(Runnable target, String name)

    ==Runnable中只有一个run();方法,实现Runnable的对象必须放在Thread中使用==

  • 使用Thread和Runnable的区别:

    1. extends Thread 的话,有一个java局限性,java是单继承,如果一个类继承了Thread,就不能继承其他的类了
    2. implements Runnable 的话,首先是通过实现接口实现,一个类可以实现多个接口,即这个类既然可以作为作为可以被线程执行的目标对象之外,还可以给这个类添加其他的类(即实现多个接口)。

extends Thread创建线程:

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
/**
* @ClassName MultiThreadingDemo01
* @Description extends Thread 创建线程
* @Author renhongchang
* @Date 2021/7/20 9:13
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class MultiThreadingDemo01 extends Thread{

//启动线程

@Override
public void run() {
super.run();
System.out.println("启动MultiThreadingDemo01的线程");
}
}

//测试类
public class MultiThreadingDemo01Test {
public static void main(String[] args) {
//实例化
MultiThreadingDemo01 myThread01 = new MultiThreadingDemo01();
MultiThreadingDemo01 myThread02 = new MultiThreadingDemo01();
//启动线程
myThread01.start();
myThread02.start();
}
}

implements Runnable创建线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @ClassName MultiThreadRunnable
* @Description Runnable创建线程
* @Author renhongchang
* @Date 2021/7/20 9:34
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class MultiThreadRunnable implements Runnable{

@Override
public void run() {
System.out.println("this is Runnable Thread");
}
}

//测试类
public class MultiThreadRunnableTest {
public static void main(String[] args) {
Thread myThread = new Thread(new MultiThreadRunnable());
myThread.start();
}
}

使用匿名内部类创建线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyThread02 extends Thread{
/**
* 使用匿名内部类创建线程
*
* @param args
*/
public static void main(String[] args) {
//循环10次,创建10个进程
for (int i = 0;i < 10;i++){
//使用匿名内部类创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
},"this name is " + i).start();
}

}
}

线程创建计时器:

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
/**
* @ClassName MultiThreadTimer
* @Description 计时器
* @Author renhongchang
* @Date 2021/7/20 9:20
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class MultiThreadTimer extends Thread{
/**
* sleep() 让线程里边的方法不运行
*/
@Override
public void run() {
super.run();
int i = 0;
while(1 == 1){
System.out.println("计时器:" + i);
i++;
try {
//线程每延迟一秒循环一次
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

//测试类
public class MultiThreadTimerTest {
public static void main(String[] args) {
//实例化线程
MultiThreadTimer myThreadTimer = new MultiThreadTimer();
//启动线程
myThreadTimer.start();
}
}

线程中的方法:

currentThread() : 获取当前线程对象;

在Thread类中定义了三个优先级常量:MIN_PRIORITY, MAX_PRIORITY 和NORM_PRIORITY,其值分别为1, 10, 5。

setPriority(int xxx) : 设置线程优先级,级别为1-10,10为优先级最高,优先级高并不是先执行,只是增加抢占到资源的机会。如果没有 为线程分配优先级,==默认为NORM_PRIORITY==。

Java采用==抢占式调度方式==,即高优先级线程具有剥夺低优先级线程执 行的权力。 如果一个低优先线程正在执行,这时出现一个高优先级线程,那么低 优先级线程就被停止执行,放弃CPU,退回到等待队列中,让高优先 级线程立即执行。 如果多个线程具有相同的优先级,则按”==先来先服务==”的原则调度。

Thread类中的==static yield()==方法用来让==当前线程主动放弃CPU==,将执 行的机会让给了下一个同级别的线程。

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
public class MultiThreadDemo02 extends Thread{

public MultiThreadDemo02(String name) {
super(name);
}

@Override
public void run() {
super.run();
System.out.println("执行了线程对象" + Thread.currentThread().getName());
}
}

//测试类
public class MultiThreadDemo02Test {
public static void main(String[] args) {
//创建线程对象
MultiThreadDemo02 myThread021 = new MultiThreadDemo02("线程1");
MultiThreadDemo02 myThread022 = new MultiThreadDemo02("线程2");
MultiThreadDemo02 myThread023 = new MultiThreadDemo02("线程3");
MultiThreadDemo02 myThread024 = new MultiThreadDemo02("线程4");

//优先级
//第一种方法 使用setPriority()方法
myThread021.setPriority(10);
myThread024.setPriority(10);
//让步
myThread021.yield();
//第二种方法
myThread022.setPriority(Thread.MIN_PRIORITY);
myThread023.setPriority(Thread.currentThread().MIN_PRIORITY);


//启动线程
myThread021.start();
myThread022.start();
myThread023.start();
myThread024.start();
}
}

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
public class MultiThreadDemo03 extends Thread {
//重写有参构造方法
public MultiThreadDemo03(String name) {
super(name);
}

//重写run()方法

@Override
public void run() {
super.run();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("线程" + Thread.currentThread().getName() + "执行结束");
}
}

//测试类
public class MultiThreadDemo03Test {
public static void main(String[] args) {
//创建线程对象
MultiThreadDemo03 multiThreadDemo031 = new MultiThreadDemo03("1111");
MultiThreadDemo03 multiThreadDemo032 = new MultiThreadDemo03("2222");

//启动线程
multiThreadDemo031.start();
multiThreadDemo032.start();

//join 插入
//等待线程终止
/*如果没有插入方法先执行则先执行主线程(main),即先输出“程序结束” ,
如果有插入方法,则先执行插入的线程,即“程序结束”在插入的线程执行之后输出*/
try {
multiThreadDemo031.join();
multiThreadDemo031.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

//主线程方法输出
System.out.println("程序结束");

}
}

终止线程:

==interrupt() :终止线程==

==isInterrupted() : 获取当前线程是否终止==

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
public class MyThread03 extends Thread{
@Override
public void run() {
super.run();
int i = 0;

while(true){
System.out.println("thread:" + i++);
//如果线程状态是终止,则结束循环
if (Thread.currentThread().isInterrupted()){
System.out.println("线程终止!");
break;
}
}
}
}

//测试类
public class MyThread03Test {
public static void main(String[] args) throws InterruptedException {
//创建线程对象
MyThread03 myThread03 = new MyThread03();
//启动线程
myThread03.start();

//阻塞20毫秒
Thread.sleep(20);
//终止线程
myThread03.interrupt();
}
}

同步锁

给代码块加锁:(锁定某个对象)

1
2
3
synchronized (){
...
}

给方法加锁:

1
public synchronized void method(){    ...}
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
//实体类
public class Bank {
String account; //构造方法
double balance; //余额 //构造方法
public Bank(String account,double balance){
this.account = account;
this.balance = balance;
}
//账户取款
public void drawAccount(String name) throws InterruptedException {
//使用同步锁 给当前 Bank对象加锁
synchronized(this){
this.balance = this.balance - 200;
//
Thread.sleep(100);
System.out.println(name + "取款200 账户余额: " + this.balance);
}
}
}
public class MyThread extends Thread {
String name; //取款人
public MyThread(String name) {
this.name = name;
}
//账号、余额 赋值
static Bank bank = new Bank("1001",2000);
//执行线程任务
@Override
public void run() {
super.run();
while (1 == 1){
if (bank.balance >= 200){
try {
bank.drawAccount(this.name);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("余额不足!!!!");
break;
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
MyThread mt1 = new MyThread("张三");
MyThread mt2 = new MyThread("李四");
MyThread mt3 = new MyThread("王五");
mt1.start();
mt2.start();
mt3.start();
}
}

死锁:

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
/** 
* @ClassName MyThread04
* @Description 死锁
* @Author renhongchang
* @Date 2021/7/20 15:46
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class MyThread04 extends Thread{
String name;
//创建两个对象
static Object obj1 = new Object();
static Object obj2 = new Object();
//构造方法
public MyThread04(String name) {
this.name = name;
}
@Override
public void run() {
super.run();
if("线程1".equals(this.name)){
synchronized(obj1){
System.out.println(this.name + "锁定了资源obj1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj2){
System.out.println(this.name + "锁定了资源obj2");
}
}
}
if("线程2".equals(this.name)){
synchronized(obj2){
System.out.println(this.name + "锁定了资源obj2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj1){
System.out.println(this.name + "锁定了资源obj1");
}
}
}
}
}
//测试类
public class MyThread04Test {
public static void main(String[] args) {
MyThread04 mt1 = new MyThread04("线程1");
MyThread04 mt2 = new MyThread04("线程2");
mt1.start();
mt2.start();
}
}

线程间通信:

多个线程之间需要协同完成工作,就需要线程之间进行通信。

wait()用于让当前线程失去操作权限,当前线程进入等待序列

notify()用于随机通知一个持有对象的锁的线程获取操作权限

notifyAll()用于通知所有持有对象的锁的线程获取操作权限

wait(long) 和wait(long,int)用于设定下一次获取锁的距离当前释放锁的时间间隔

在使用的时候要求==在synchronize语句中使用==

wait() : 让当前线程等待,知道另一个线程调用此对象的notify() 或者调用此对象的 notifyAll() 方法,或者指定的时间以过。当前线程必须锁定此对象

notify() : 唤醒正在等待对象的单个线程。换新的线程将以通常的方式争夺此对象资源;

notifyAll() : 唤醒正在等待对象的所有线程。

包子铺生产包子,消费者吃包子的问题:

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
/** 
* @ClassName Bun02
* @Description 包子实体类
* @Author renhongchang
* @Date 2021/7/21 9:41
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class Bun02 {
private String skin; //包子皮
private String fill; //包子馅
private boolean flag; //包子状态
//包子数量
private int count = 0;
//最多容纳四个包子 private static final int VOLUME = 4;
//包子皮
private String[] skins = {"白皮","绿皮"};
//包子馅
private String[] fills = {"韭菜馅","大葱馅"};
//无参构造
public Bun02() {
}
//getter方法
public String[] getSkins() {
return skins;
}
public String[] getFills() {
return fills;
}
public static int getVOLUME() {
return VOLUME;
}
//setter & getter方法
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getSkin() {
return skin;
}
public void setSkin(String skin) {
this.skin = skin;
}
public String getFill() {
return fill;
}
public void setFill(String fill) {
this.fill = fill;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
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
/** 
* @ClassName Producer02
* @Description 包子铺生产包子
* @Author renhongchang
* @Date 2021/7/21 9:41
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class Producer02 extends Thread{
//创建一个包子变量
private Bun02 bun02;
public Producer02(String name,Bun02 bun02) {
super(name);
this.bun02 = bun02;
}
//布置线程任务,生产包子
@Override
public void run() {
super.run();
while(true){
synchronized(bun02){
if (!bun02.isFlag()){
if (bun02.getCount() % 2 == 0){
bun02.setSkin(bun02.getSkins()[0]);
bun02.setSkin(bun02.getFills()[0]);
System.out.println(Thread.currentThread().getName() + "生产了" + bun02.getSkins()[0] + bun02.getFills()[0] + "的包子");
System.out.println("生产了" + (bun02.getCount() + 1) + "个包子了"); }else{
bun02.setSkin(bun02.getSkins()[1]);
bun02.setSkin(bun02.getFills()[1]);
System.out.println(Thread.currentThread().getName() + "生产了" + bun02.getSkins()[1] + bun02.getFills()[1] + "的包子");
System.out.println("生产了" + (bun02.getCount() + 1) + "个包子了");
}
//生产一个包子,包子数量加一
bun02.setCount(bun02.getCount() + 1);
//生产一个休息一会
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果生产足够的包子,则唤醒吃货
if (bun02.getCount() >= bun02.getVOLUME()){
//设置包子状态
bun02.setFlag(true);
System.out.println("包子生产结束!!快来吃吧");
bun02.notify();
}
}else {
//如果有包子,等待
try {
bun02.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
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
/** 
* @ClassName Consumer02
* @Description 吃货吃包子
* @Author renhongchang
* @Date 2021/7/21 10:34
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class Consumer02 extends Thread{
//创建一个包子变量
private Bun02 bun02;
public Consumer02(String name,Bun02 bun02) {
super(name);
this.bun02 = bun02;
}
//布置线程任务,吃包子
@Override
public void run() {
super.run();
//如果有包子,一直吃包子
while(true){
synchronized(bun02){
if (bun02.isFlag()){
//吃货吃包子
if (bun02.getCount() % 2 == 0){
//每吃一个包子,包子数量减1
bun02.setCount(bun02.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "吃了" + bun02.getSkins()[0] + bun02.getFills()[0] + "的包子");
System.out.println("剩下" + bun02.getCount() + "个包子了");
}else{
bun02.setCount(bun02.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "吃了" + bun02.getSkins()[1] + bun02.getFills()[1] + "的包子");
System.out.println("剩下" + bun02.getCount() + "个包子了");
}
//休息一会
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果包子数量没有,唤醒生产者
if (bun02.getCount() == 0){
//设置包子状态
bun02.setFlag(false);
System.out.println("包子吃完了,在来几个吧");
bun02.notify();
} }else {
//如果没有包子,等待
try {
bun02.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 
* @ClassName Bun02Test
* @Description 测试类
* @Author renhongchang
* @Date 2021/7/21 10:42
* @Version 1.0
* @Blog https://rhc-rgb.github.io
*/
public class Bun02Test {
public static void main(String[] args) {
//创建包子对象
Bun02 bun = new Bun02();
//创建包子铺线程
Producer02 producer01 = new Producer02("李阿姨",bun);
producer01.start();
Producer02 producer02 = new Producer02("王阿姨",bun);
producer02.start();
//创建吃货线程
Consumer02 consumer01 = new Consumer02("张三",bun);
consumer01.start();
Consumer02 consumer02 = new Consumer02("李四",bun);
consumer02.start();
}
}

线程池:

对于更复杂的任务来说这种频繁手动式的创建、管理线程显然是不可取的,因为线程对象使用了大量的内存,在大规模应用程序中,创建、分配和释放多线程对象会产生大量内存管理开销。为此,可以考虑使用Java提供的线程池来创建多线程,进一步优化线程管理。

==Executor接口==实现线程池管理

步骤:

  1. 创建一个实现Runnable接口或者Callable接口的实现类,同时重写run()或者call()方法;
  2. 创建Runnable接口或者Callable接口的实现类对象;
  3. 使用Executors线程执行器类创建线程池;
  4. 使用ExecutorService执行器服务类的submit()方法将Runnable接口或者Callable接口的实现类对象提交到线程池进行管理;
  5. 线程任务执行完成后,可以使用shutdown()方法关闭线程池。

Executors创建线程池的方法

方法声明 功能描述
ExecutorService newCachedThreadPool() 创建一个==可扩展线程池的执行器==。这个线程池执行器适用于启动许多短期任务的应用程序
ExecutorService newFixedThreadPool(int nThreads) 创建一个==固定线程数量线程池的执行器==。这种线程池执行器可以很好的控制多线程任务,也不会导致由于响应过多导致的程序崩溃
ExecutorServicenewSingleThreadExecutor() 在特殊需求下创建一个==只执行一个任务的单个线程==
ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个==定长线程池,支持定时及周期性任务执行==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RunnableImp implements Runnable{   
@Override public void run() {
System.out.println(Thread.currentThread().getName() + "执行一个线程任务");
}
}
public class ThreadPool {
public static void main(String[] args) {
//创建一个固定线程数量线程池的执行器。这种线程池执行器可以很好的控制多线程任务,也不会导致由于响应过多导致的程序崩溃
ExecutorService executor = Executors.newFixedThreadPool(2);
//将Runnable接口的实现类提交到线程池进行管理
executor.submit(new RunnableImp());
executor.submit(new RunnableImp());
//线程池长度是2 所以不能添加第三个新的线程进去
executor.submit(new RunnableImp());
//关闭线程池
executor.shutdown();
//执行线程任务 (线程池已经关闭,因此不能在提交线程任务)
executor.submit(new RunnableImp());
}
}

CompletableFuture类实现线程池管理

CompletableFuture对象创建的四个静态方法:

方法声明 功能描述
static CompletableFuture runAsync(Runnable runnable) 以Runnable函数式接口类型为参数,并使用ForkJoinPool.commonPool()作为它的线程池执行异步代码获取CompletableFuture计算结果为空的对象
static CompletableFuture runAsync(Runnable runnable, Executor executor) 以Runnable函数式接口类型为参数,并传入指定的线程池执行器executor来获取CompletableFuture计算结果为空的对象
static CompletableFuture supplyAsync(Supplier supplier) 以Supplier函数式接口类型为参数,并使用ForkJoinPool.commonPool()作为它的线程池执行异步代码获取CompletableFuture计算结果非空的对象
static CompletableFuture supplyAsync(Supplier supplier, Executor executor) 以Supplier函数式接口类型为参数,并传入指定的线程池执行器executor来获取CompletableFuture计算结果非空的对象