新启线程的两种方式
继承Thread
class UserThread : Thread(){
override fun run() {
super.run()
println("I extend thread")
}
}
fun main(){
val userThread = UserThread()
userThread.start()
}
实现Runnable接口
class UserRunnable : Runnable{
override fun run() {
println("I implement runnable")
}
}
fun main(){
val userRunnable = UserRunnable()
Thread(userRunnable).start()
}
Thread和Runnable的区别
Thread是Java里对线程的唯一抽象,Runnable只是对任务(业务逻辑)的抽象。Thread可以接受任意一个Runnable的实例并执行。
通过继承Thread启动线程的缺点:
-
java只能单继承,继承了Thread就不能继承其他类,限制了扩展性
-
这种方式会使任务和线程对象紧密耦合
-
每次执行新任务都要new一个Thread对象,创建和销毁的开销大
-
代码结构较差,不符合”组合优于继承“的原则
通过实现Runnable启动线程的优点
-
可以继承其他类,更灵活
-
让任务和线程对象解耦。一个任务可以被多个线程执行
-
可以更好地配合线程池,线程池可以复用已创建的线程,效率更高
-
代码结构更好,更面向对象
如何中止线程
-
不推荐使用的方法:stop() 原因:stop方法不会给线程正常释放资源的时间
-
推荐使用的方法: interrupt()
-
用法: 在调用interrupt()方法后,线程通过isInterrupted()或Thread.interrupted()方法判断是否被中断
-
原理: 调用线程的interrupt()方法就是将该线程的中断标志位改为true,然后通过检查方法来判断该线程是否 被中断。
-
注意:如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait等),则当线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后会立即将线程的中断标示位清除,即重新设置为false。
示例代码

输出结果

所以如果你自己自定义一个取消标志位来中断线程,是无法在阻塞状态下感知到你改变了取消标志位,也就无法及时中断线程。
线程的常用方法
start()
在这个方法中,我们创建的Thread实例才真正地和操作系统里面的线程挂钩,等分配到cpu才会调用实现的run()方法。
run()
实现业务逻辑的地方,可以多次调用。
yield()
让出该线程占用的cpu,但让出的时间不可设定,也不会释放锁。
join()
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。 比如在线程 B 中调用了线程 A 的 join()方法,直到线程 A 执行完毕后,才会继续执行线程 B。
wait()
调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断 才会返回.需要注意,调用 wait()方法后,会释放对象的锁。可以设置等待时间,时间一过,退出等待状态,继续执行下面代码。
notify()/notifyAll()
通知一个/所有等待在该对象上的线程,没竞争到锁的线程继续等待。
线程间的共享
synchronized内置锁
-
作用:保证同一时间只有一个线程访问,适合多个线程同时进行读写的场景。
-
错误加锁:在同步块里面改变了加锁对象的地址
-
用法: 通过加锁,保证打印顺序的正确

volatile
-
作用:保证了不同线程对这个变量的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。适合一写多读的场景。
-
用法: 如果不添加Volatile注释,会导致子线程不知道ready的变化,从而一直陷入循环。

ThreadLocal
-
作用:为每一个线程都提供了变量的副本,适用于不想要多线程共享的场景。
-
用法:如果不用ThreadLocal,就会导致用户名称,请求Id与线程名称无法对应,对应关系将会变得混乱。


-
内存泄漏的原因:使用完ThreadLocal后不调用remove()方法
-
错误用法:修改ThreadLocalMap存放的对象的属性值,这会导致所有使用此对象的线程都会修改。
线程安全
死锁
-
触发条件:1.两个及以上的线程争夺两个及以上的资源;2.争夺资源的顺序不对;3.拿到资源不放手
-
解决方案:1.让争夺资源的顺序一致;2.使用尝试拿锁的机制,拿到一把锁后尝试拿第二把,如果没拿到就会释放第一把锁
活锁
-
触发原因:两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到第一把锁后,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。
-
解决方案:每个线程休眠随机数,错开拿锁的时间。
10万+

被折叠的 条评论
为什么被折叠?



