一、什么是进程与线程?以及为什么要使用多线程?
进程
可以简单的理解为正在运行的程序,是线程的容器
进程是操作系统资源分配的最小单元。一个进程拥有的资源有⾃⼰的堆、栈等资源(例如jvm进程)
线程
从属于进程,一个进程可以包含很多个线程
线程是操作系统能够进行运算调度的最小单元。是进程中实际运行的单位。一个进程中每个线程执行不同的任务 。
每个java程序至少包含两个线程
一个main线程
一个垃圾回收线程
为什么使用多线程
使用多线程主要是提高CPU的利用率,加快任务执行效率
二、并发与并行概念
并发Concurrent
同一时刻有多个任务(指令)在单个Cpu上交替执行
针对单核CPU提出
并行parallel
同一时刻有多个任务(指令)在多个CPU上同时执行
针对多核CPU提出
三、多线程的同步与异步
同步
按照顺序一步步执行代码,同步指的是线程之间的执行顺序是按照代码的顺序依次执行的,
即一个线程需要等待另一个线程完成某个操作后才能继续执行。在同步模式下,线程之间共享数据,需要使用锁等机制来确保数据的一致性和安全性。
异步Future
代码获取结果需要分开执行,异步指的是线程之间的执行是相互独立的,不需要等待其他线程的完成。
在异步模式下,线程之间不共享数据,每个线程有自己的独立工作空间,通过消息队列或回调函数等方式来进行线程间的通信和数据传递。
四、创建线程的三种方式以及优缺点及注意事项
1.继承Thread类:
通过继承Thread类,并重写其run()方法来创建线程。然后使用该子类的实例调用start()方法启动线程。
优点:
简单直接,代码结构清晰。
缺点:
因为Java不支持多重继承,继承了Thread类的子类不能再继承其他类。线程与代码的耦合紧密,任务与线程的关系较为固定。
2.实现Runnable接口:
定义一个类实现Runnable接口,并实现其run()方法。然后创建Thread的实例,将Runnable实例作为参数传递给Thread的构造方法,并调用start()方法启动线程。
优点:
线程与任务之间解耦,任务可以通过Runnable接口独立定义。
可以实现资源共享,多个线程可以共享同一个Runnable实例。
缺点:
代码稍微复杂一些,需要显式地创建Runnable实例,再传入Thread的构造方法。
3.实现Callable接口:
类似于实现Runnable接口,但是run()方法没有返回值,而是使用Callable的call()方法执行任务,并返回一个结果。
优点:
可以获取线程执行任务的返回值。
可以抛出异常,方便错误处理。
缺点:
创建线程时需要使用ExecutorService的submit()方法调度任务。
1.使用目的:拿到线程的执行结果
2.创建方式: 搭桥
3.获取结果: get(阻塞方法:会阻塞程序的运行)
4.start与run的区别 :
start会启动线程,run不会只是普通的调用方法
5.线程不允许多次启动
6.线程是由cpu来调度的,start之后,只是进入就绪状态
Thread类中的方法
sleep 线程休眠
Thread.currentThread 返回当前线程对象
获取线程名称
静态方法Thread.currentThread.getName()
成员方法getName
设置线程名称
静态方法Thread.currentThread.setName()
成员方法setName
五、线程操作共享数据时的安全问题
当多个线程去访问一个公共的资源时,就会出现资源数值错误
问题出现的原因:
存在非原子操作
什么叫原子操作:
所有操作要么一起执行,要么一起失败
解决方法:加锁
synchronized关键字同步方法
synchronized同步代码块
Lock锁
synchronized锁
加synchronized锁实现线程间的同步,保证原子操作
synchronized的缺陷
效率低
锁的释放情况少(同步代码块中执行的非常耗时的操作)
试图获取锁时不能设定超时
不够灵活
不能自己控制加锁和解锁的时机
每一个锁仅有单一的条件(某个对象)
无法知道是否成功获取到锁
同步锁:锁的是方法
互斥锁:线程互斥 同时只能有一个线程执行加锁的方法
非公平锁:线程抢锁的概念是随机的
隐式锁:看不到锁
可重入锁: 同一个线程抢到锁之后可以再次获取该锁
加锁的步骤:
1.首先所有线程参与抢锁
2.成功抢到的线程执行加锁的方法
3.释放锁
显示锁 Lock锁
ReetrantLock
可重入锁
死锁
死锁就是指两个或两个以上的线程在执行过程中,由于竞争资源产生的互相等待资源释放的现象
具体原因
互斥条件:一个资源只能被一个线程占有,当这个资源被占有后其他线程就只能等待
不可剥夺条件:当一个线程不主动释放资源时,此资源一直被拥有线程占有
请求并持有条件:线程已经拥有了一个资源后,又尝试请求新的资源
环路等待条件:产生死锁一定是发生了线程资源环形链
解决死锁
改变资源的环路等待,顺序获取锁资源