Java多线程
目录
- 继承Thread类
- 实现Runnable接口
- 使用匿名内部类的形式
- 使用lambda表达式
- 使用Callable和Future创建
- 使用线程池
- Spring的@Async注解
使用Callable和Future创建
futureTask的get方法之所以会阻塞是因为实现方法中使用了LockSupport.park()
方法
|
|
线程安全
synchronized
用法:
- 修饰代码块 :指定加锁对象,进入同步代码块前要获得给定对象的锁
- 修饰实例方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 修饰静态方法:作用于当前类对象(类.class)加锁,进入同步代码块的时候需要获得当前类对象的锁
线程同步
线程如何保证同步,即如何保证线程安全性问题
- 使用
synchronized
锁,注意锁升级过程 - 使用
Lock
锁,锁升级 - 使用T
hreadLocal
,需要注意内存泄漏的问题 - 使用原子类,
CAS
非阻塞
线程通讯
等待/通知机制
wait()
:线程阻塞并且释放锁notify()
:通知一个在对象上等待的线程,使其返回main()方法继续执行。返回的前提是改线程已获得了对象的锁。notifyAll()
:通知所有等待在该对象的线程
需要结合synchronoized
关键字来使用:两个线程之间的通信,对于同一个对象来说,一个线程调用对象的wait()
方法,一个线程调用对象的notify()
方法,这个对象本身就需要同步。所以在调用wait()
和notify()
方法之前,需要使用synchronized
关键字同步对象。
join()
join
底层原理是基于wait()
方法封装。
唤醒?
线程的状态
- 初始化状态
- 就绪状态
- 运行状态
- 死亡状态
- 阻塞状态
- 超时等待
- 等待状态
守护线程和用户线程
Java中的线程分为两种:守护线程和用户线程
创建出来的线程,默认为用户线程,通过Thread.setDaemon(true)来将线程设置为守护线程。
守护线程依赖于用户线程,用户线程退出了,其中的守护线程也就退出了,典型的额守护线程—垃圾回收线程。
用户线程是独立存在的,不会应为其他用户线程的退出而退出。
安全的停止一个线程
- stop():终止线程,并且清楚监控器锁的信息。
- destory():
- interrupt():打断正在运行或者正在阻塞的线程。
- 如果目标线程在调用
Object class
的wait()
、wait(long)
或wait(long, int)
方法、join()
、join(long, int)
或者sleep(long, int)
方法时被阻塞,那么interrupt
会生效,改线程中断状态将会被清除,抛出InterruptedException
异常。 - 如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被终端或者返回特殊的异常值,达到终止线程的目的。
- 如果以上条件都不满足,则会设置此线程为中断状态。
- 如果目标线程在调用
- 标志位
Lock锁
使用ReentrantLock
实现同步
lock()
方法上锁unlock()
方法释放锁
使用Condition
实现等待/通知 类似于wait()
和notify()
、notifyAll()
Condition
|
|
yield()
该方法会是线程主动放弃CPU的执行权
- 多线程
yield()
会让线程从云心状态进入到就绪状态,让后调度执行的其他线程来竞争CPU。 - 具体的实现依赖于底层操作系统的任务调度器
优先级
- 在Java语言中,每个线程都有优先级,当线程调控器有机会选择新的线程时,线程的优先级越高,越有可能被执行。优先级可以设置1~10。数字越大代表优先级越高。(Oracle为Linux提供的Java虚拟机中,线程的优先级被忽略,即所有线程具有相同的优先级)。所以不能过度的依赖优先级
- 线程的优先级用数字来表示。默认范围是1~10.即
Thread.MIN_PRIORITY
到Thread.MAX_PRIORITY
。 - 如果CPU非常繁忙,优先级越高的线程获得更多的时间片,但是CPU空闲时,设置优先级几乎没有任何作用。
问题
Join()/wait()和sleep()的区别
-
sleep(long)
方法在睡眠时不释放对象锁。 -
join(long)
方法先执行另外一个线程,在等待的过程中释放锁,底层是基于wait()方法实现的。 -
wait(long)
方法在等待的过程中会释放锁。
wait()/notify()在Object类中的原因
使用wait()方法的时候需要结合synchronized关键字,synchronized这把锁可以是任意对象,所以任意对象都可以调用wait()和notify()。