Java多线程
多线程概述
线程与进程
进程
- 是内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
- 值进程的执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
- 线程实际上是在进程基础上的进一步划分,一个进程启动之后,里面若干的执行路径又可以划分成若干个线程
线程调度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eoKunGnP-1620203162745)(/Users/faro_z/Library/Application Support/typora-user-images/image-20210504211610442.png)]
分时调度
- 所有线程,轮流使用 CPU,平均分配每个线程占用 CPU 的时间
抢占式调度
- 当 CPU 空闲的时候,CPU 会抛出一个时间片,谁抢到就是谁的。优先让优先级高的线程使用 CPU;如果线程优先级相同,那么,会随机选择一个(线程随机性),Java 使用的就是抢占式调度
- CPU 使用抢占式调度模式在多个线程之间高速切换,对于 CPU 而言,某时刻,只能执行一个线程,而 CPU 在多个线程切换的速度很快,在我们看来,就像是多个线程在同时执行一样。其实多线程程序并不能提高程序的运行速度(甚至会因为切换的时候的时间消耗,使得总耗时还边长),但是能提高程序的运行效率,让 CPU 使用率更高。
同步与异步
**同步:**排队执行,效率低但是安全
**异步:**同时执行,效率高但是数据不安全
并发与并行
并发:指两个或多个事件,在==同一时间段==内发生。(比如说一天内,一小时内)
**并行:**指两个或多个事件,在同一时间发生(同时发生)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZDhhlwB-1620203162747)(/Users/faro_z/Library/Application Support/typora-user-images/image-20210504212449234.png)]
线程阻塞
一般,比较耗时的操作,可以看成是线程阻塞
比如说,我们要等待用户输入,那么,那一段线程,就是阻塞了
Java 多线程的实现
java 中实现多线程,一共有三种方法
- 继承 Thread 类
- 实现 Runnable 接口
- 实现Callable 接口
继承 Thread 类
public class ThreadDemo {
public static void main(String[] args) {
new ThreadClass().start();
for (int i = 0; i < 10; i++) {
System.out.println("锄禾日当午"+i);
}
}
}
class ThreadClass extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}
}
每个线程都有自己的栈空间,但是共用一个堆空间
实现 Runnable 接口
public class RunnableDemo {
public static void main(String[] args) {
//子线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}, "子线程").start();
//主线程(的一部分,整个 main 都是主线程)
for (int i = 0; i < 10; i++) {
System.out.println("锄禾日当午"+i);
}
}
}
实现 Callable<T>
接口
Callable,是带返回值的线程
上面两个实现方法,可以看成主线程之外的子线程去完成任务,和主线程无关了
但是 Callable,相等于主线程让子线程去完成任务,等到子线程任务完成了,再给主线程返回一个结果。比如说,主线程可以派发 1000 个任务,给 1000 个线程,等着 1000 个任务执行完毕了,可以获得 1000 个结果。
Callable 的使用步骤如下:
- 编写实现 Callable 接口,实现 call 方法
class Xxx implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
- 创建 FutureTask 对象,并传入编写的 Callable 对象
FutureTask<Integer> task = new FutureTask<>(callable);
- 通过 Thread,启动线程
new Thread(task).start
获取子线程返回结果的方法如下:
- 使用 FutureTask 的
get()
方法
**注意:**使用get()
,会让主线程等待子线程执行完毕,然后去获得方法
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable cl = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(cl);
//执行子线程
new Thread(task).start();
Integer res = task.get();
System.out.println("子线程计算结果为:"+res);
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
System.out.println("main"+i);
}
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("子线程正在执行"+i);
}
return 100;
}
}
- 使用使用 FutureTask 的
isDone()
方法
可以先判断子线程有没有执行完,执行完了,再去获取子线程的返回值,避免主线程等待
public class CallableDemo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable2 cl = new MyCallable2();
FutureTask<Integer> task = new FutureTask<>(cl);
new Thread(task).start();
for (int i = 0; i < 10; i++) {
Thread.