线程
操作系统进行运算调度的最小单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
线程的三种创建方式:1、继承Thread 2、实现Runnable接口 3、实现Callable接口(异步执行,可以获取返回值和异常信息)
线程的状态以及切换:
锁
1、synchronized
java的关键字,能够将方法或者代码块锁起来,实现同步的功能,保证同一时间只能被一个线程访问。
修饰普通代码、修饰代码块、修饰静态变量
修饰静态变量为类锁,其余都是对象锁,类锁与对象锁是不冲突的。
可重入性:线程获取了锁的时候,再访问同一个对象(对象锁)或者类(类锁)的时候就不需要再取获取锁了
锁释放:1、执行完毕 2、出现异常
死锁:多个同步资源被同时占有被循环等待,比如 同步资源 t1,t2 线程r1占用t1等待t2, 线程r2占用了t2等待t1
死锁检测:
1、jstack -F pid
2、JConsole工具
死锁预防与解决:
1、需要多个锁的时候,将顺序固定
2、使用显示锁Lock并设置超时时间
缺陷:
1、没法设置超时时间
2、排他锁,只是读操作也会上锁
2、lock
ReentrantLock 可重入锁
ReadWriteLock 读写锁
ReentrantReadWriteLock 可重入读写锁
线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1),Executors.defaultThreadFactory());
初始化一个线程的核心参数(从左到右)
corePoolSize:核心线程数
maximumPoolSize:线程池最大线程数
keepAliveTime:非核心线程数的存活时间
unit:非核心线程数的存活时间单位
workQueue:工作队列,存储等待处理的任务
任务提交过程:
工作线程的任务执行完后会不断从阻塞任务任务中获取任务去执行。
很高级的实现:ctl(AtomicInteger) 高三位存储线程池的状态 低三位存储工作线程数;工作线程数的改变会伴随着线程池状态的改变,借助AtomicInteger保存在一个值中可以保证操作的原子性。
不错的文章:objcoding.com/2019/04/25/…
volatile
保证可见性
数据在计算时会从主存拷贝一份到高速缓存中计算,计算完刷回内存,在出现并发计算的情况下,会导致数据计算的不准备 例如 (a=0) a = a+1 两个线程同时把a=1加1后刷回内存,最后的值是2,期望的应该是3
线程对volatile变量的修改会立刻被其他线程所感知,即不会出现数据脏读的现象,从而保证数据的可见性