多线程-初阶
Posted 秃头小宝儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程-初阶相关的知识,希望对你有一定的参考价值。
多线程编程
1.线程(Thread)
进程是系统分配资源的最小单位,线程是系统调度的最小单位。一个进程内的线程之间是可以共享资源的。
每个进程至少有一个线程存在,即主线程。
(1)面试题:进程VS线程
1.进程是系统分配资源的最小单位;线程是系统调度的最小单位。
2.一个进程中可以包含多个线程,并且至少包含一个线程(主线程)。
3.线程的存储必须依赖于进程。
4.进程的实际执行单位是线程。
5.进程不可以共享资源,而线程可以共享资源。
6.进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈。
7.线程的创建、切换及终止效率更高。
(2)线程的创建方式
①方式一:继承Thread类
// 写法1:
static class MyThread extends Thread {
@Override
public void run() {
// 写你的业务代码
// 打印当前线程的名称
System.out.println("子线程名称:" +
Thread.currentThread().getName());
}
}
//写法2:
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("当前线程名称:" +
Thread.currentThread().getName());
}
};
// 运行线程
thread.start();
缺点:因为Java语言的设计式单继承的,当我继承了Thread类之后,就不能继承其他类。
②方式二:实现Runnable接口
//写法3:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程名:" +
Thread.currentThread().getName());
}
}
public class ThreadDemo5 {
public static void main(String[] args) {
// 创建 Runnable 子对象
MyRunnable myRunnable = new MyRunnable();
// 创建线程
Thread thread = new Thread(myRunnable);
// 启动线程
thread.start();
}
}
//写法4:
// 创建一个匿名 Runnable 类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("当前线程:" +
Thread.currentThread().getName());
}
});
thread.start();
//写法5:
// lambda + runnable (只有JDK 1.8+才支持)
Thread thread = new Thread(() -> {
System.out.println("线程名:" +
Thread.currentThread().getName());
});
thread.start();
③方式三:Callable+Future接收线程执行之后的返回值
/**
* 创建一个有返回值的线程
*/
public class ThreadDemo8 {
// 创建线程,Callable可以返回进程的结果
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 产生随机数
int num = new Random().nextInt(10);
System.out.println(String.format("线程:%s,生产了随机数:%d",
Thread.currentThread().getName(), num));
return num;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1.创建 Callable 子对象
MyCallable callable = new MyCallable();
// 2.使用 FutrueTask 接收 Callable
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 3.创建线程并设置任务
Thread thread = new Thread(futureTask);
// 执行线程
thread.start();
// 得到线程的执行结果
int num = futureTask.get();
System.out.println("线程返回结果:" + num);
}
}
(3)线程休眠(sleep)
(4)创建多少线程合适?
看实际情况,任务分为两种:
1.计算密集型任务;(线程的数量=CPU核数最好)
2.读写文件;(理论上讲线程数越多越好)
2.Thread 类及常见方法
(1)Thread 的常见构造方法
(2)Thread 的几个常见属性
- ID 是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况,下面我们会进一步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束了
- 线程的中断问题,下面我们进一步说明
(3)面试题:start() 和 run()
- 1.run()方法是一个对象的普通方法,它使用的是主线程来执行任务的。
- 2.start()是线程的开启方法,它使用新的线程来执行任务的。
- 3.start()方法只能执行一次,而run()可以调用n次。
(4)线程中断的方式
1.自定义全局标识来实现中断;
2.使用Thread的interrupated()方法来中断。
注意:使用系统的Interrupated() 可以及时的终止线程,而使用自定义全局变量终止线程的方式,比较温柔不能在里面终止。
(5)判断线程终止的方式
3.线程的状态
(1)线程的所有状态
(2)线程的状态和转移
注意: 线程常用方法:yield()[用来让出CPU的执行权]
yield分配执行权不一定成功,要看CPU的最终选择,但总体来说还是基本符合预期的。
4.线程不安全的因素
- 1.CPU是抢占式执行的(万恶之源)
- 2.多个线程同时操作一个变量
- 3.内存可见性
- 4.非原子性
- 5.指令重排序
- 6.编译器优化(编译器优化在复杂的多线程中会出现混乱,从而导致线程不安全的问题。)
非线程安全:使用多线程执行任务,最终得到的结果和预期不一样。
以上是关于多线程-初阶的主要内容,如果未能解决你的问题,请参考以下文章