线程任务
Runnable
接口定义了任务,但若想实现线程行为,必须将任务附着在线程上,因此创建线程Thread
并将一个Runnable
对象交给该线程,启动Thread
的start
对象将为该线程执行必须的初始化操作,并调用Runnable
对象的run
方法执行任务。
守护线程
Daemon
线程在后台提供服务,也称后台线程。当所有非后台线程结束时,程序结束,所有后台线程会被即刻终止,因此若在后台线程的代码中添加finally
想要优雅的关闭后台线程是无法实现的。
后台线程的设置setDaemon()
必须在线程启动前设置,在后台线程中创建的线程也会默认为后台线程
原子性和易变性
对于除了long
和double
类型,其他基本类型的简单操作(读取、写入)都是原子性的,因为long
和double
类型占了8个字节64bit,会造成字节撕裂,即32位操作系统会对64位的的读取写入分成两个32位的读取写入,造成问题。
volatile
保证可见性和有序性但无法保证原子性,具体原因看我这篇博文
原子操作
java通过锁和循环CAS实现原子操作
通过自旋循环CAS直到成功,但CAS仍存在三大问题:
- ABA。即原值从A改变成B又变成A,使用CAS以为没有变化,实际变化了。解决思路为增加版本号。
- 循环时间长开销大。如果长时间不成功,会给CPU造成很大开销。
- 只能保证一个共享变量的原子操作。解决方案是使用锁或将多个共享变量合并成一个共享变量。
线程中断
中断只是设置中断标志位,能够进入到catch中断异常处理语句,但不是说线程就到此终结了,若在catch中设置了return,则返回到方法入口,方法消亡。否则继续往下执行。若先中断后面遇到sleep,则立即抛出中断异常,并将中断标志清空。
yield()
sleep()
方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,sleep()
方法允许较低优先级的线程获得运行机会
yield()
方法使当前线程让出CPU占有权,但让出的时间是不可设定的,也不会释放锁标志。yield()
只是使当前线程重新回到可执行状态,所有执行yield()
的线程有可能在进入到可执行状态后马上又被执行,所以yield()
方法只能使同优先级的线程有执行的机会,在一个运行系统中,如果较高优先级的线程没有调用sleep()
方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。