1.2 多线程
一.线程实现📛
Thread \ Runnable \ Callable
1.1 Thread
- 继承 Thread 类并重写 run 方法
- 创建线程对象,调用 start 启动线程
1.2 Runnable(避免了单继承的局限性)
- 自定义类实现 Runnable 接口,并重写 run 方法
- 新建 Thread 对象,传入自定义类实例参数
* Thread.currentThread().getName()
获取当前线程名
1.3 rambda表达式
(避免内部类定义过多)
new Thread(()->{
...
}).start();
二.线程状态
方法 | 说明 |
---|---|
setPriority() | 更改线程优先级 |
sleep() | 指定时间内线程休眠 |
join() | 线程加入,直到结束再继续执行当前线程 |
yield() | 暂停当前线程,CPU重新调度 |
interrupt() | 中断线程 |
isAlive() | 判断线程是否处于活动状态 |
setDaemon() | 守护线程,默认为普通线程 |
三.线程同步📛
3.1 synchronized(隐式)
- 使用 synchronized 定义同步方法,默认锁的是this
- synchronized方法控制对"对象"的访问,每个对象对应一把锁
- 对象调用锁才能执行,否则该线程阻塞
- 方法执行为独占状态,方法结束释放锁
- 缺陷🔔:会一定程度的影响效率
private synchronized void buy(){}
synchronized (obj){...}
3.2 死锁
定义🔒:多个线程相互抱着对方的锁,导致自身的锁无法释放
解决方法:切忌在当前锁未释放的情况下,准备锁上另一个对象
产生死锁的四个必要条件:(破除一个即可避免死锁)
- 互斥条件:一个资源每次只能被一个进程使用;
- 请求与保持条件:一个进程因请求资源而堵塞时,对自己所获资源不放;
- 不剥夺条件:进程已获得资源,未使用完成不能剥夺;
- 循环等待条件:若干进程之间形成头尾相接的循环等待关系
3.3 Lock(显式)
ReentrantLock 类实现 Lock : 与 synchronize 相同的并发性
注:使用 try 保证Lock能打开锁
3.4 对比
- Lock是显示的,需要手动开关锁;scn 是隐式的,作用域结束自动释放
- Lock只有代码块锁;scn 有代块码锁和方法锁
- Lock性能较好
四.线程通信
4.1 生产者和消费者
方法 | 说明 |
---|---|
wait() | 表示线程等待,与sleep不同,会释放锁 |
wait(long time) | 指定时间内等待 |
notify() | 唤醒一个处于等待的线程 |
notifyAll() | 唤醒一个对象上所有调用wait方法的线程 |
* 以上方法只能在同步方法或同步代码块中作用,否则会抛出异常
4.2 线程池
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完成返回线程池。可避免多次创建销毁,提高复用率。
线程池API:ExecutorService 和 Executors;
方法 | 说明 |
---|---|
void execute() | 一般用执行Runnable |
void shutdown() | 关闭连接池 |