join、sleep、wait、notify,run()和start()线程相关方法的区别

发布时间 2023-09-06 17:46:03作者: Sharley

run()和start()方法区别

  • run():方法只是线程的主体方法,和普通方法一样,不会创建新的线程。

  • start():只有调用start()方法,才会启动一个新的线程,新线程才会调用run()方法,线程才会开始执行。

wait、notify、notifyAll 区别

  • wait():释放obj的锁,导致当前的线程等待,直接其他线程调用此对象的notify()或notifyAll()方法。
  • notify(): 唤醒在此对象监视器上等待的单个线程,只能在同步控制块或方法里使用
  • notifyAll(): 通知所有等待该竞争资源的线程,只能在同步控制块或方法里使用

注意:当要调用wait()或notify()/notifyAll()方法时,一定要放到synchronized(obj)代码中,否则会报错java.lang.IllegalMonitorStateException。当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此等待线程虽被唤醒,但仍无法获得obj锁,直到调用线程退出synchronized块,释放obj锁后,其他等待线程才有机会获得锁继续执行。

join、sleep、wait 区别

  • join():方法在等待的过程中释放对象锁。
  • sleep():来自Thread类;不释放对象锁;任何地方可使用;使用sleep()必须捕获异常;等待一定时间自动醒来,进入可运行状态;一个线程对象调用了sleep()后,不会释放所持有的所有对象锁,所以不影响其他进程对象的运行;静态,只对当前对象有效。
  • wait():来自object类;释放对象锁;使其他线程可使用同步控制块或方法,自身只能在同步控制块或方法里使用;不需要捕获异常;一旦调用,必须notify或notifyAll唤醒;若线程拥有某对象同步锁,调用wait()后,此线程会释放持有的同步资源,而不限于这个被调用了wait()的同步锁。

volatile、synchronized、Lock、ReentrantLock 区别

  • volatile:解决变量在多个线程间的可见性,但不能保证原子性,只能用于修饰变量,不会发生阻塞。volatile能屏蔽编译指令重排,不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。多用于并行计算的单例模式。volatile规定CPU每次都必须从内存读取数据,不能从CPU缓存中读取,保证了多线程在多CPU计算中永远拿到的都是最新的值。
  • synchronized:互斥锁,操作互斥,并发线程过来,串行获得锁,串行执行代码。解决的是多个线程间访问共享资源的同步性,可保证原子性,也可间接保证可见性,因为它会将私有内存和公有内存中的数据做同步。可用来修饰方法、代码块。会出现阻塞。synchronized发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生。非公平锁,每次都是相互争抢资源。
  • lock:是一个接口,lock可以让等待锁的线程响应中断。在发生异常时,如果没有主动通过unLock()去释放锁,则可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
  • ReentrantLoc:可重入锁,锁的分配机制是基于线程的分配,而不是基于方法调用的分配。ReentrantLock有tryLock方法,如果锁被其他线程持有,返回false,可避免形成死锁。对代码加锁的颗粒会更小,更节省资源,提高代码性能。ReentrantLock可实现公平锁和非公平锁,公平锁就是先来的先获取资源。ReentrantReadWriteLock用于读多写少的场合,且读不需要互斥场景。

Thread为什么不能用stop方法停止线程

从官方文档可以得知,调用Thread.stop()方法是不安全的,这是因为当调用Thread.stop()方法时,会发生下面两件事:

  • 1:即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。
  • 2:释放该线程所持有的所有的锁。调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。