Thread 类常用的 API 方法
-
start(): 线程的启动
-
run():线程的运行(由start()方法自动调用)
-
stop():线程的停止(不建议使用)
-
setPriority():设置线程优先级
-
setName():设置线程的名字
-
isAlive():测试线程是否处于活动状态
通过 Runnable 接口构造线程
Runnable 接口:可以将CPU 代码和数据分开,形成清晰的模型,还可以从其他类继承。
Thread 接口:编写简单,直接继承,重写run方方法,不能再从其他类继承。
多线程的同步控制
线程间的互斥:: 同时运行的几个线程需要共享一些数据,共享的数据,在某一个时刻只允许一个线程访问。
典型的如:生产者/消费者 问题
**synchronize **:线程同步关键字,实现互斥
-
用于指定需要同步的代码段或方法,也就是监视区
-
可实现与一个线程的互斥
-
首先判断对象的锁是否存在,如果在就获得锁,用完再释放锁。
同步与锁的要点
-
只能同步方法,不能同步变量
-
每个对象只有一个锁
-
类可以同时拥有同步和非同步方法,非同步方法不受锁的限制。
-
线程休眠时,它所持有的任何锁都不会被释放。
-
线程可以获得多个锁
-
同步会损害并发性,应该尽可能缩小同步的范围。
-
需要说明是获取哪个对象的锁
线程的等待与唤醒
wait()方法:线程的等待
notify(): 随机唤醒一个等待的线程
notifyAll(): 唤醒所有等待的线程
后台线程
也叫守护线程,通常是为了辅助其他线程而运行的线程
它不妨碍程序的终止。
使用 setDaemon()
设置后台线程。
线程的生命周期与死锁
一个线程在任何一个时刻都处于某个状态中。Java 线程的状态有以下几种:
-
New:新创建的线程,尚未执行;
-
Runnable:运行中的线程,正在执行run()方法的Java代码;
-
Blocked:运行中的线程,因为某些操作被阻塞而挂起;
-
Waiting:运行中的线程,因为某些操作在等待中;
-
Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
-
Terminated:线程已终止,因为run()方法执行完毕。
线程的几种基本状态:诞生、就绪、运行、阻塞、休眠、死亡状态。
死锁: 多个线程互相等待对方释放的资源,此即为死锁。
结束线程的声明:通过控制 run 方法中的循环条件的方式来结束一个线程,不建议使用 stop 方法。
线程的调度
控制多个线程在同一个 CPU 上以某种顺序运行称为线程调度。
每个 java 线程都有一个优先级,其范围在 0-10 之间,默认情况下,每个线程的优先级都设置为 5。
基于优先级的线程调度
-
对具有同优先级的线程,java 的处理是随机的。
-
高优先级的线程优先执行。
setPriority() 方法: 设置线程优先级
小结
-
线程的同步方法
-
线程的声明周期
-
线程的优先级
线程安全与线程兼容
java的线程安全
-
不可变: 如 final 修饰数据、String 类型数据、枚举类型数据、Long、Double
-
绝对的线程安全:
-
相对的线程安全:
线程兼容和线程对立
线程对立:无论调用端是否采用了同步措施,都无法保证两个线程的安全运行。
如何实现线程安全
互斥同步: 临界区、互斥量、信号量
synchronized 关键字 :对自己是可重入的,不会把自己锁死。
ReentrantLock 重入锁:性能比 synchronized 更好
非阻塞同步:
-
使用基于冲突检测的乐观并发策略。
-
使用硬件处理器指令进行不断重试策略(JDK1.5 以后)
无同步方案:
-
可重入代码:也称为纯代码。
-
线程本地存储。
如何进行锁优化
锁优化源自于 jdk6
自旋锁: 为了让线程等待,让线程进行自旋锁,java 默认是自旋 10 次。
自适应自旋:自旋时间不固定
锁消除:不需要加锁的就进行锁消除
锁粗化: 包含更多的代码
偏向锁:在无竞争的情况下把整个同步都消除掉。
每天学习一点点,每天进步一点点。