什么是多线程?
简单来说就是可以cpu可以在多个程序中来回切换运行
什么是并发和并行?
并发:多个指令在一个cup上交替执行
并行:多个指令在一个cup上同时进行
一个cup上有多个线程,也就是说一个线程可以运行一个程序,也可以运行多个程序
一个线程运行一个程序不用切换叫并行,一个线程运行多个程序要来回切切换叫并发
多个线程也是一样的,运行的程序一旦超过线程数就要交替运行,这个就要并发,如果没有超过,一个线程对应一个叫并行
多线程实现的方式
1:继承Thread这个类开启多线程
2:实现Runnable这个接口来实现多线程
3:实现Callable接口和Future接口实现多线程
步骤:
1:创建一个类实现Callable接口并重写这个接口的方法,这个方法是多线程要执行的方法
2:创建这个类的对象表示多线程要执行的任务或者程序
3:创建Future接口的实现类对象FutureTask把这个类的对象传递进去来管理这个多线程执行的结果
4:创建Thread对象把实现类对象FutureTask传递进去表示线程
5:开启线程
多线程常用的成员方法
线程的名字
1:可以不用设置,自动增加,不过默认是从0开始的
2:如果设置有两个方式,一个是利用构造方法,一个是利用set方法
获取当前线程的对象
使用静态方法currentThread
线程的休眠时间
1:用sleep这个方法可以设置线程在哪个地方停多久
线程的优先级
抢占式调度:线程抢夺cup的执行权执行
非抢占式调度:线程轮流执行,每个线程执行的时间都差不多
jvm采用的就是抢占式调度,线程的优先级越高,抢占cup的可能性越大,那这个线程执行的就越快,java把线程的优先级分为10档,最小的是1,最大的是10
守护线程
守护线程就是等非守护线程执行完毕后陆续结束
礼让线程
表示让出cup的调度,重新抢cup的使用权
插入线程
表示插入哪个线程之前,等插入的这个线程全部执行完毕后在执行被插入的那个线程
线程的生命周期
执行资格:有抢cup的资格
执行权:抢到cup并执行
线程运行时可能被抢占:在抢占式系统中,即使线程正在运行,也可能因时间片耗尽、高优先级线程出现或中断触发而被强制切换。
线程睡眠时必然被抢占:线程进入睡眠状态后,CPU资源立即释放,其他线程可立即获得执行权。
线程安全的问题
线程在抢到cup执行代码时,有可能被其他线程抢到然后执行别的线程的代码
可以利用同步代码块来解决这个问题,可以把共享的数据给锁起来,也称为锁对象,这个对象任意都可以
同步代码块注意的问题:
1:锁一定要唯一,这锁是共享的
2:不能把锁写在循环外面,因为只有一个线程执行完之后,都得重新抢cup的调度
同步代码块的写法
我们要把执行的代码写在锁里面,注意不能把循环也写在里面,这样就只能一个线程来执行这个代码,其他线程进不去
同步方法
格式:
如果想把所有的方法都锁起来,这时候用同步方法就可以了
同步方法写的套路技巧
1:先写循环
2:同步代码块
3:判断数据是否到了末尾(如果到了退出,没到继续运行)
4:把同步代码块里面的代码抽取成一个方法并把synchronized这个关键字,删除同步代码块
Lock锁
在同步代码块和同步方法里面我们不能手动上锁和解锁
在同步代码块里面,进入那个代码块就自动上锁,出来自动解锁,同步方法也是一样的,运行那个方法自动上锁,运行结束自动解锁,无法手动上锁和解锁,那个run方法里面的代码结束线程也就结束了
Lock锁就可以解决锁的问题
死锁
两个锁嵌套导致死锁
等待唤醒机制(生产者和消费者)
wait这个方法相当于绑定锁和解除cup的执行权等待被唤醒
代码如下
等待唤醒机制的核心是两个线程执行两个方法时,这两个方法里面的锁必须是一样的,这样这把锁才能共享
利用堵塞队列来实现等待唤醒机制来实现代码
线程的状态
java其实就6种状态
练习题一:
练习题二:
练习题三:
线程池
简单来说就是自动创建线程执行任务
1:当我们创建一个任务交给线程池时,线程池会自动创建一个线程去执行这个任务,执行完了之后,再把线程还到线程池当中,在下次执行任务时,直接复用已经创建好的线程,当我们有多个任务时,线程池会创建多个线程并执行,我们可以限定线程池中的线程数
2:如果线程池中没有多余的线程,任务就会等待分配线程
线程池对象创建的代码实现
创建一个没有上限的线程池,其他他是有上限的,是int的最大值
第一种创建一个没有上限的线程池
第二个创建一个有上限的线程池
自定义线程池:
创建ThreadPoolExecutor这个类的对象
七个参数
参数一:核心线程数
参数二:最大线程数(核心线程数+临时线程数)
参数三:空闲线程最大存活时间(临时线程数超过多少时间自动销毁)
参数四:时间单位(用TimeUnit来指定)
参数五:任务队列(当核心线程池用了之后,后面的任务能排多少)
参数六:创建线程工厂
参数七:任务的拒绝策略(当核心线程,任务队列,临时线程都被用了之后还要任务的拒绝策略)
当我们的核心线程数为3个,最大线程数为6个,这时临时线程数为3个,任务队列为3个,当我的任务超过3个,超过的任务首先进入任务队列进行等待,任务队列满了之后,启动临时线程来运行,核心,任务队列临时线程都满了之后,这时启动任务的拒绝策略
一个线程对应一个任务,直到这个任务执行完毕,线程在返回线程池,但是多个线程执行多个任务时,可能执行的顺序不一样,因为要抢占cup的执行权,但是执行过程中任务是不会更改,最后线程池还要手动关闭,因为线程池里面的线程产生后即使没有线程也会存活,因为他们是非守护线程,jvm必须等待他们执行完毕或者手动关闭才会停止
代码展示
线程池多大合适呢
我们用java来获取可用的线程数
我们用公式来计算