@author ergwang
https://www.cnblogs.com/ergwang/
文章末尾附pdf和png下载链接
currentThread() 获取当前线程的信息
isAlive() 验证当前线程是否存活
sleep(long millis) 线程休眠
线程堆栈相关
StackTraceElement[] getStackTrace()
获取一个标识该线程堆栈跟踪元素数组
dumpStack()
将当前线程的堆栈跟踪信息输出至标准错误流
(该方法只用于调试)
Map<> getAllStackTraces()
返回所有活动线程的堆栈跟踪的映射
getId() 获取线程的唯一标识、Id
停止线程的几种方式
使用退出标志使流程正常退出,
如在线程中用使用break、抛异常等
使用stop()强行终止
【可能会丢数据,已被弃用】
使用interrupt()方法中断线程
执行这个方法后,会将线程标记为中断,
【但还会继续执行,直到执行完毕】
interrupted()
判断线程是否已经中断
【执行后,会将中断线程状态清除!】
this.isInterrupted()
判断this关键字所在的类的对象是否已中断
线程的暂停与重启
suspend() 暂停线程
缺点:【独占资源】【易造成数据不完整】
resume()
重启线程
yield() 释放当前线程所占用的CPU资源
线程优先级
getPriority() 获取当前线程的优先级
【最大10 ,最小1】
setPriority() 设置当前线程的优先级
MIN_PRIORITY = 1;
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;
优先级具有【继承性】
A线程启动B线程,则B优先级等于A
setDaemon(true) 设置当前线程为守护线程
Java中的线程分为:
守护线程
用户线程(非守护线程)
方法内的变量永远线程安全
实例变量线程不安全,怎么解决
同步方法
同步代码块
synchronized在字节码指令中的原理
同步方法:
在class文件中标记了ACC_SYNCHRONIZED
同步代码块:
class文件中用monitorenter和monitorexit分别表示同步代码块的开始和结束
脏读问题
发生脏读:
因为读取成员变量(实例变量)时,此值已经被其他线程修改了
synchronized 锁重入
锁重入:
可以重复加锁,
有几个锁加几个锁,一层套一层
支持父子类继承
重写synchronized方法
抛异常,锁自动释放
println() 也是同步方法,线程安全
【同步方法】锁是对象的锁,不同的对象是不同的锁
【缺点】一个对象中多个同步方法,用的同一个锁(都是对象锁),Thread1调用A方法一直没执行完毕,从而阻塞Thread2调用的B方法。
同步和异步
区别和联系
同锁 -> 同步
异锁 -> 异步
各有什么优缺点
同步执行 成员变量线程安全 但效率低
异步执行 成员变量线程不安全 效率高
什么时候用同步/异步?
同步代码块相比于同步方法的优势
同步方法的锁: 同步方法所在类的对象
同步代码块的锁: 很灵活, 可以是当前方法所在类的对象,也可以是其他类对象(比如Class类对象,String类对象等)
锁对象
this
同步方法的锁对象
类名.class
静态同步方法的锁对象
其他类对象
【注1】String做锁对象时
【注2】锁对象修改对线程同步的影响
死锁问题
public class MyService{
public synchronized static void method1(){}
public static void method2(){
ssynchronized(MyServcie.class){}
}
public synchronized void method3(){}
public static void method4(){
ssynchronized(this){}
}
public static void method5(){
ssynchronized("abc"){}
}
}
(A) method1 和 method2 持有同一把锁,即:MyService.java 对应的 Class类对象(其实就是Class对象)
(B) method3 和 method4 持有同一把锁,即:MyService.java类的对象 (等同于this)
(C) method5 持有的锁是字符串对象
原子性
可见性
B线程马上就能看到A线程修改的数据
【原理】用volatile或者synchronized修饰的方法,修改读取变量时,强制从公共堆栈读,不从线程私有堆栈中读取。
【注】线程修改数据,写都是写到公共堆栈中
禁止代码重排序
原子性
32位系统中,未用volatile声明的long和double数据类型是非原子的,64位则要根据具体实现判断。
【注】无论32位还是64位,无论用不用volatile声明,i++ 都是一个非原子操作,除非用AtomicInteger声明变量
可见性
禁止代码重排序
synchronized关键字
作用:保证同一时刻,只有一个线程能执行某个方法或代码块。
修饰:可以修饰方法、代码块。
特征:可见性、原子性、禁止代码重排序。
使用场景:多个线程对同一个对象中的同一个成员变量变量操作时,为了避免出现线程安全问题时使用。
volatile关键字
主要作用:让其他线程能看到修改后的最新的值。
修饰:只能修饰变量。
特征:可见性、原子性、禁止代码重排序。
使用场景:欲实现一个变量在被A线程修改后,其他线程立马能获取到最新值时候,就用volatile修饰这个变量。
原理
持有相同锁(对象级别的)的多个线程,在wait()处暂停执行,释放锁,直到接到通知,notify() 或者 notifyAll()再获取锁,继续执行。
【注】锁必须是对象级别的,因为wait(), notify(), notifyAll()是Object类中的,不是Thread类中的方法,所以必须要对象。
wait()
暂停当前线程,释放锁
【注】执行wait()方法后,马上暂停线程么,并释放锁
notify()
发出通知,wait状态的线程可以准备获取锁,开始执行了,只能通知“一个”线程,唤醒顺序同执行wait()顺序一致
【注】执行notify()方法后,不是马上暂停当前线程,而是要将当前线程同步代码块执行完毕后,才释放锁,故wait状态的线程也要等它执行完毕才能抢到锁
notifyAll()
发出通知,wait状态的线程可以准备获取锁,开始执行了,默认按照执行wait()相反的顺序依次唤醒全部线程
【注】锁释放时机同notify()
释放锁的时机
wait(long time)
如果线程超过time时间没有被唤醒,则自动醒来,但是要执行的前提条件是再次持有锁,没有持有锁的话,一直等待,直到拿到锁才开始执行
time的单位为毫秒,其他用法一致
wait() 与 sleep()的区别
wait() 线程阻塞,立即释放锁
sleep() 线程阻塞,不 释放锁
用while替代if
执行wait()后,线程阻塞,当线程醒来的时候
生产者消费者模型的几种实现
不带缓冲区
带缓冲区
管道流通信
【案例】利用wait/notify实现数据库交叉备份
使用场景
join() 原理
join() 与 sleep() 的区别
join() 是线程间通信,释放锁 (主线程执行wait(),子线程执行完毕后通知主线程)
sleep() 是线程内部通信,不释放锁
join(long time)
join() 和 interrupt() 同时使用出现异常
join(long time) 后面的代码可能提前运行,其实就是锁的问题
原理及其作用
验证
值继承
对象继承
重写childValue() 方法,对值进行加工
重进入锁
相比于synchronize关键字实现线程间同步,ReentrantLook更加灵活,如具有嗅探锁定、多路分支通知等功能
线程间通信
结合Condition对象实现线程间通信
利用Condition对象的await()和signal() 方法实现wait()/notify()机制
线程对象注册在不同的Condition中,可以实现有选择性地进行线程通知(多路分支通知)
【注】必须在condition.await() 之前调用lock.lock()获取锁,因为Condition对象的await()方法执行后会将线程转换为wait状态,并释放锁
await() 方法暂停线程运行的原理
(这一块暂时不理解)
生产者消费者模型
公平锁与非公平锁
公平锁
非公平锁
实现
ReentrantLock默认是非公平锁
构造公平/非公平锁的构造函数
public ReentrantLock(boolean fair)
true => 公平锁
false => 非公平锁
API
public int getHoldCount()
查询“当前线程”保持锁定的个数,
即调用lock()方法的次数
public final int getQueueLength()
获取正等待获取此锁的估计数,
【注】已经获取锁的不算
public int getWaitQueueLength(Condition condition)
public final boolean hasQueuedThread(Thread thread)
public final boolean hasQueuedThreads()
public final boolean hasWaiters(Condition condition)
public final boolean isFair()
public boolean isHeldByCurrentThread()
public final boolean isLocked()
public void lockInterruptibly()
public boolean tryLock()
public boolean tryLock(long timeout, TimeUnit unit)
public boolean await(long timeout, TimeUnit unit)
public long awaitNanos(long nanosTimeout)
public boolean awaitUntil(Date deadline)
public void awaitUninterruptiably()
读写锁
原理与使用
读操作相关的锁——共享锁
写相关的锁——排他锁
读写分离,提高系统效率
因为读几乎不需要同步,大家都可以读,但是写必须保证数据同步,所以可以只加“写锁”,不加“读锁”
Demo
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
……
lock.readLock().lock();
lock.writeLock().lock();
……
lock.readLock().unlock();
lock.writeLock().unlock();
读读共享
写写互斥
读写互斥
写读互斥
创建Timer对象的时候,会启动一个新的非守护线程TimerThread,且线程中存在一个死循环导致线程一直运行,可以调用timer.cancel()方法终止计时器
timer.cancel()方法
在多任务的Timer对象中调用时,会优先清空任务队列(已经在执行的任务不受影响),然后再销毁进程
多任务Timer对象中执行task任务的算法
每次最后一个执行的放入队列头,
如:第一次 ABC,
第二次 CAB,
第三次 BCA
执行情况
正常执行
单个TimerTask任务
多个TimerTask任务,但时间没有交集,
立即执行
计划执行的时间早于当前时间
延时执行
因为前面的任务可能消耗的时间比较长,后面的任务就会被延后
schedule(TimerTask task, Date time)
schedule(TimerTask task, Date firstTime, long period)
schedule(TimerTask task, long delay)
schedule(TimerTask task, long delay, long period)
以当前时间点为基准,delay毫秒后
按照间隔时间无限循环执行任务
scheduleAtFixedRate(TimerTask task, long firstTime, long period)
与schedule()方法一样,只是加了【追赶性】
【大白话】这个方法就是要把无论因为某某原因导致延迟、没执行的任务,全部补上,补上后就和schedule()一样了
最好只能有一个对象存在的场景时使用单例模式。
如:1. 日志系统,只需要一个日志系统记录全局的
2. id生成器,保证id唯一性,单例更合适
3. 计时器、计数器,都是保证数据准确
4. 多数多线程的线程池,方便线程管理
5. 数据库连接,资源重用,减少频繁开关的资源消耗
饿汉模式 / 立即加载
类加载的时候实例化
优点:线程安全
缺点:
懒汉模式 / 延迟加载
DCL 双检查锁
第一次调用时实例化
优点:延迟加载,可能节省资源
缺点:必须实现同步,否则线程不安全
DCL——Double-Check Locking,双检查锁
保证线程安全,提高多线程下效率
用volatile修饰实例变量的必要性
实现实例变量线程间可见
禁止实例化时代码重排序
静态内部类
第一次调用时实例化
优点:
线程安全
延迟加载
缺点:
实例化时不能传参
实现原理:
利用类加载的特点(即:外部类加载时,并不需要立即加载内部类,内部类不被加载则不实例化)实现单例实例化和延迟加载,又因为静态内部类线程安全的特性,保证了整体的线程安全。
【内部类线程安全的原理不理解,摘抄自网上】
内部类线程安全的原理:
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行()方法后,其他线程唤醒之后不会再次进入()方法。同一个加载器下,一个类型只会初始化一次。)
static代码块
原理
利用静态代码块的特性(使用类时加载静态代码块)实现
优点:
延迟加载
线程安全
缺点:不能传参
【我感觉】这东西就是饿汉模式实现了延迟加载,也是一种懒汉模式的实现吧
enum枚举
原理
利用枚举类的特性(使用枚举类时,自动调用其构造方法)实现
优点:延迟加载、线程安全
缺点:不能传参
和静态代码块实现差不多呀。。。。
使用默认反序列行为
单例模式下的对象也会变成多例
保证序列化和反序列化后单例的条件:
状态信息存储在Thread.State枚举类中
线程的五种状态
NEW
RUNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
线程的生命周期
线程组实现和特性
ThreadGroup类
一级关联(常用)
多级关联(不常用)
线程自动归组属性
ThreadGroup类中的API
activeCount()
enumerate(ThreadGroup array[])
将当前线程组中的子线程 复制 到数组中
enumerate(ThreadGroup array[], boolean recurve)
true 递归复制,false 非递归
ThreadGroup.getParent()
Thread类中的API
activeCount()
enumerate(Thread tarray[])
再次实现线程执行有序性
异常处理
异常处理优先级
手机扫一扫
移动阅读更方便
你可能感兴趣的文章