1. 多线程
2. 并行和并发
并行:在同一时刻,有多个指令在多个cpu上同时执行
并发:在同一时刻,有多个指令在单个CPU上交替执行
3. 进程和线程
3.1. 进程
进程:是正在运行的软件
独立性:进程是一贯能独立运行的基本单位
动态性:进程的实质是程序的一次执行过程
并发性:任何进程都可以同其他进程一起并发执行
3.2. 线程
线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程只有一条执行路径
- 多线程:一个进程拥有多个执行路径
4. 继承Thread
类继承Thread类
重写run()方法
public static class xian extends Thread {
@Override
public void run() {
super.run();
}
}
创建类对象
调用用start()方法 启动线程 交由JVM调用此线程的run()方法
public static void main(String[] args) {
xian x = new xian();
x.start();
}
5. Runnable接口
类实现Runnable接口
重写run方法
public static class xian2 implements Runnable{
@Override
public void run() {
System.out.println("线程启动");
}
}
创建类对象
xian2 x =new xian2();
创建Thread类对象,把类对象作为构造方法的参数
Thread t1 =new Thread(x);
启动线程
t1.start();
6. Callable接口与Future
类实现Callable接口
重写call()方法
// 接口的泛型是call方法返回的类型
public static class xian3 implements Callable<String> {
@Override
public String call() throws Exception {
//返回的为线程执行完毕的结果,执行语句在方法体写
return "你好多线程";
}
}
创建类对象
xian3 x = new xian3();
创建Future的实现类FutureTask对象,并将类对象作为构造方法参数传递
FutureTask<String> ft = new FutureTask<>(x);
创建Thread类对象,并把FutureTask对象作为构造方法参数传递
Thread t1 = new Thread(ft);
启动线程
t1.start();
// 获取线程执行完毕的结果,get方法一定在线程启动之后,否则get下面语句不执行
String s = ft.get();
System.out.println(s);
7. Thread
7.1. getName
获取线程名称,线程有默认名称为 Thread-线程数
7.2. setName
也可以通过构造方法设置线程名称
Thead类中带有带参构造方法,可以给线程设置名称,但是继承的类必须使用super关键字引用.
7.3. currentThread
返回当前正在执行的线程对象
String name = Thread.currentThread().getName();
如果Runnable接口或Callble接口想要使用Thread的方法可以
先捕抓到当前执行的线程 再使用Thread的方法
8. 线程休眠
sleep()方法 让线程休眠指定毫秒
Thread.sleep(1000);
9. 线程调度
- 分时调度模型:所有线程轮流使用cou的使用权,平均分配每个线程占用cpu的时间片
- 抢占式调度模型:优先让优先级高的线程使用cpu,如果优先级相同,那么会随机选择一个,优先级高的线程获取cpu时间片相对多一些
10. 线程的优先级
10.1. getProiority
获取指定线程的优先级
Thread.currentThread().getPriority()
10.2. setPriority
设置指定线程的优先级
默认为5 范围1-10
Thread.currentThread().setPriority(6);
优先级高不代表运行的时间相对减少
11. 后台线程/守护线程
11.1. setDaemon
需要传递一个布尔值,true为设置为守护线程
当普通线程执行完后,那么守护线程没有继续执行下去的必要(自动结束)
12. 线程的安全问题
12.1. 同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
Object obj =new Object();
// 传递任意对象,注意要是唯一的,否则线程认为是不同的同步锁
synchronized (obj){
// 线程共享的操作数据
}
默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭,只有等代码块执行完毕才重新打开
同步的好处:
解决了多线程的数据安全问题
弊端:
当线程过多时,因为每个线程都会判断同步上的锁,耗费系统资源,运行效率降低
12.2. 同步方法
在方法返回值前面加上关键字synchronized,该方法的所有代码都加上锁
此同步方法锁对象为this
如果此同步方法 是静态的 则锁对象为 类名.class
13. Lock锁
Lock是接口不能直接实例化,我们通过它的实现类ReentrantLock来实例化
private ReentrantLock lock =new ReentrantLock();
13.1. lock
lock.lock();
加锁
13.2. unlock
lock.unlock();
释放锁
14. 死锁
线程死锁是指由于两个或者多个线程互相持有对象所需要的资源,导致这些线程处于等待状态,无法前往执行
15. 生成者消费者(等待 唤醒机制)
15.1. 等待 wait
同步代码块中锁的对象是什么则 wait方法在该对象调用
15.2. 唤醒 notify
会随机唤醒该进程里的任意线程
15.3. 唤醒所有 notifyall
锁的步骤:
- while(true) 死循环
- synchronized 同步锁,锁对象要唯一
- 判断,共享数据是否结束 结束的操作
- 判断,共享数据是否结束 没有结束的操作
16. 阻塞队列实现等待唤醒机制
16.1. ArrayBlockingQueue
底层是数组,有界,创建时通过带参构造方法,定义该阻塞队列的边界
16.1.1. put
put(“元素”),存储,底层有lock锁
16.1.2. take
take()取出,如果取不出则会一直等待,直到下一关元素put进阻塞队列
16.2. LinkedBlockingQueue
底层是链表,无界.但不是真正的无界,默认最大为int的最大值,也可以通过带参构造方法,定义阻塞队列的边界