简介

是指从软件或者硬件上实现多个线程并发执行的技术

具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能

简单来说就比如你正在听歌,但是你想要在听歌的同时发布评论并且听歌不受影响,这就是多线程了,一共两个线程,一条线程供你听歌不干其他的事情就光顾着你听歌就行,而另一条线程则是供你发布评论,两条线程同时工作,但又互不影响

概念

并发和并行

  • 并行:在同一时刻,有多个指令在多个CPU上同时执行

    一共三件事情,同时执行是为并行

  • 并发:在同一时刻,有多个指令在单个CPU上交替执行

    还是三个事情,在第一个事情上搞一会,然后在第二个事情上在在搞一会,然后跑到第三个事情上,来回在这三个事情上搞是为并发

进程和线程

  • 进程:正在运行的软件
    1. 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
    2. 动态性:进程的实质是程序的一次执行过程,进程是动态产生的,动态消亡的
    3. 并发性:任何进程都可以和其他的进程一起并发执行
  • 线程:是进程中的单个顺序控制流,是一条执行的路径
    1. 单线程:一个进程如果只有一条执行的路径,则被称为单线程程序
    2. 多线程:一个进程如果有多个执行路径,则被成为多线程程序

多线程实现

一个有三种实现方式:

  1. 继承Thread类的方式进行实现
  2. 实现Runnable接口的方式进行实现
  3. 利用CallableFuture接口的方式实现

继承Thread类

  1. 定义一个类继承Thread
  2. 在类中重写run()方法
  3. 创建类的对象,启动线程

单线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
Run r = new Run();
r.start();
}
}

class Run extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

run方法里面写的内容就是线程开启之后要执行的事情

因为只有一条线程也可以算是单线程

多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static void main(String[] args) {
// 创建第一条线程
Run r1 = new Run();
// 创建第二条线程
Run r2 = new Run();
// 开启第一条线程
r1.start();
// 开启第二条线程
r2.start();
}
}

class Run extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

因为线程的执行是CPU来决定的,所以第一次运行和第二次运行的结果可能不一样,线程之间的执行有一个随机性

小问题

run和stage方法有什么区别

run方法这个用来重写是因为里面的内容是线程要执行的内容所以要重写

run和stage方法有什么区别

run:调用run方法是就跟调用普通方法一样,当执行完成第一个方法之后在进行执行第二条——并没有开启线程

stage:启动线程,然后由虚拟机调用run方法

实现Runnable接口

  1. 定义一个类实现Runnable接口
  2. 在类中重写run方法
  3. 创建类的对象
  4. 创建Thread类的对象,把之前创建的类对象作为构造方法的参数,启动线程

单线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
// 创建了参数对象
Run r = new Run();
// 创建线程对象,把参数传递给线程
Thread thread = new Thread(r);
thread.start();
}
}

class Run implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
Run r1 = new Run();
Thread thread1 = new Thread(r1);
thread1.start();

Run r2 = new Run();
Thread thread2 = new Thread(r2);
thread2.start();
}
}

class Run implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

Callable和Future

  1. 定义一个类并实现Callable
  2. 在类中重写call()方法
  3. 创建类对象
  4. 创建Future的实现类FutureTask对象,把类对象作为构造方法的参数
  5. 创建Thread类的对象,把FutureTask对象作为构造方法的参数
  6. 启动线程

单线程

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.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 线程开启之后需要执行的call方法
Run r = new Run();
// 可以获取线程执行的结果,也可以作为参数传递给Thread
FutureTask<String> ft = new FutureTask<>(r);
// 创建线程对象
Thread thread = new Thread(ft);
// 开启线程
thread.start();
// 获取线程执行结束返回的值
System.out.println(ft.get());
}
}

class Run implements Callable<String> {
@Override
public String call() throws Exception {

for (int i = 0; i < 100; i++) {
System.out.println(i);
}
// 返回值表示线程运行完毕之后的结果
return "执行完毕";
}
}

注意:ft.get()不可在thread.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
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Run r1 = new Run();
FutureTask<String> ft1 = new FutureTask<>(r1);
Thread thread1 = new Thread(ft1);

Run r2 = new Run();
FutureTask<String> ft2 = new FutureTask<>(r2);
Thread thread2 = new Thread(ft2);

thread1.start();
thread2.start();
System.out.println(ft1.get());
System.out.println(ft2.get());
}
}

class Run implements Callable<String> {
@Override
public String call() throws Exception {

for (int i = 0; i < 100; i++) {
System.out.println(i);
}
// 返回值表示线程运行完毕之后的结果
return "执行完毕";
}
}

三种实现方式

类型优点缺点
实现类扩展性强,实现该接口的同时开可以继承其他的类变成相对复杂,不能直接使用Thread类中的方法
继承类编程比较简单,可以直接使用Thread类中的方法可扩展性较差,不能在继承其他的类