- 并行: 同一时间点同时发生
- 并发: 同一时间段内发生
- 进程: 正在运行的程序的实例
- 线程: 是进程内部的一个独立执行单元
一个进程可以同时并发运行多个线程 - 多线程: 多个线程并发执行
线程创键
1. 继承Thread类
-
自定义一个类继承Thread
-
重写run()方法
-
创键自定义线程类的实例
-
开启线程
2. 实现Runnable接口 -
自定义一个类实现Runnable接口
-
实现run()方法
-
同过Thread执行自定义线程类
-
开启线程
3. 实现Callable接口
FutureTask介绍
常用的Future的方法: -
判断任务是否完成:isDone()
-
中断任务:cancel()
-
获取人去执行结果:get()
- 创建FutureTask实例,创建线程实例
- 创建Thread实例,执行FutureTask
- 获取并打印线程执行结果
4. 线程池(Executor)创键线程
- 通过Executors获取线程池实例对象
ExecutorService executorService = Executors.newFixedThreadPool(10);
- 通过线程池对象获取线程并执行线程对象
executorService.execute(new MyRunnable());
5.创建方式优劣
线程生命周期
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
- 新建:就是刚使用new方法,new出来的线程;
- 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
- 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
- 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
线程安全
多个线程并行正确操作共享数据,并且得到预期的值,这是线程安全的.
模拟卖票
开启了3个线程对一个数据进行操作,根据工作台打印的信息可以看出
不同的窗口卖出的同一张票,并且有窗口卖出了错误的票.
一般多个线程对变量只有读的操作没有写的操作,这个线程是安全的.
若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
线程同步 Synchronized
解决线程安全问题
方式(一):同步代码块 在代码块声明上 加上synchronized
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
方式(二):同步方法 在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AGihWFLk-1589965804694)(en-resource://database/1419:1)]
同步方法中的锁对象是 this
静态同步方法: 在方法声明上加上static synchronized
静态同步方法中的锁对象是 类名.class
public static synchronized void method(){
可能会产生线程安全问题的代码
}
死锁
同步锁使用的弊端: 当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。
Lock接口
//创建Lock锁对象
Lock ck = new ReentrantLock();
ck.lock() //创建锁
ck.unlock() //释放锁
sleep(): 释放CPU使用权,睡眠,不能被唤醒
wait(): 释放CPU使用权,在等待的时间可以被唤醒
notify(): 满足条件之后唤醒wait()暂停的线程
notifyAll(): 唤醒所有等待的线程