Java并发编程篇
Java并发编程
多线程
Java多线程的安全性
- 原子性:atomic包和synchronized修饰。
- 可见性:volatile和synchronized
- 有序性:happens-before原则来确保有序性
线程的创建方式
- 继承thread
- 实现Runnable,用Runnable对象创建线程
- 实现Callable与FutrueTask,用FutureTask对象创建线程
- 线程池
线程停止运行方式
异常法停止:线程引入interrupt()方法,在run()方法中判断interrupt()状态。抛出异常
沉睡停止:先sleep,在interrupt()判断,抛出异常。
stop()暴力停止
使用return停止线程:也是引用interrupt判断。return退出run方法
Java线程的状态
- RUNNABLE 就绪状态
- BLOCKED 等待锁
- WAITING 等待另一线程
- TIMED_WAITING 指定等待时间
- NEW 线程创建,还未调用start方法
- TEMINATED 终止状态
sleep和wait的区别
- sleep是静态,任何线程都可以直接thread.sleep调用。wait得是,Object类的实例方法,必须通过对象调用。
- sleep,sleep不会释放锁。wait会释放锁。
- sleep等待时间结束后,线程自动就绪。wait得notify,或notifyall唤醒。
BLOCKED和WAITNG的区别
- BLOCKED,是等待锁的释放。获取锁后就转为RUNNABLE.
- WAITING,是认为设置的状态,需要用notify激活。
notify选择哪个线程?
notify唤醒的线程是任意的,依赖于具体的JVM。
JVM有很多实现,比较流行的是hotspot。hotspot是“先进先出”的顺序唤醒。
不同线程之间怎么通信
synchronized 和 volatile。
用violate修饰共同能看见的字段。
一,用静态变量当锁,用wait,notify机制等待释放锁。
二,ReentrantLock,condition包替代synchronized。condition.await 类似 wait . signal 类似notify ,signalAll 类似 notifyAllBlockingQueue 线程安全的队列。
线程间通信方式有哪些?
Object的wait notify notifyAll
Lock Condition:await,signal,signalAll
volatile
CountDownLatch。一个同步辅助类。
一个计数器.CountDownLatch(int count)需要等待的线程数量。
countDown:减少计数器
await:等待,直到计数器为0.CyclicBarrier。让一组线程互相等待,直到达到某个屏障。有些类似于CountDownLatch的相反操作。等待的线程到达一定数量,才集体唤醒。
Semaphore。计数信号量。用于控制同时访问某个共享资源的线程数。
通过acquire获取许可,release方法释放许可。没有许可会被阻塞。
Synchronized和ReentrantLock的区别
Synchronized是JVM层面的内置锁。
ReentrantLock是java.util.concurrent.locks包的一个类。简单场景用synchronized(语法简洁、异常安全)
复杂并发用ReentrantLoc(可中断、超时锁、公平锁、多条件队列、定时锁)Synchronized,wait,notify
ReentrantLock ,lock ,unlock
synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁:
- 用法不同:synchronized 可用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用在代码块上。
- 获取锁和释放锁方式不同:synchronized 会自动加锁和释放锁,当进入 synchronized 修饰的代码块之后会自动加锁,当离开 synchronized 的代码段之后会自动释放锁。而 ReentrantLock 需要手动加锁和释放锁
- 锁类型不同:synchronized 属于非公平锁,而 ReentrantLock 既可以是公平锁也可以是非公平锁。
- 响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。
- 底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。
Synchronized原理
synchronized是Java提供的原子性内置锁,这种内置的并且使用者看不到的锁也被称为监视器锁,
使用Synchronized之后,会在同步的代码块加上monitorenter和monitorexit字节码指令。
执行monitorenter指令会尝试获取锁。如果对象没被加锁或获得了锁,锁的计数器+1。其他线程等待。执行monitorexit指令释放锁,锁的计数器-1。其他线程竞争锁。
Reentrantlock工作原理
ReentrantLock 的底层实现主要依赖于 AbstractQueuedSynchronizer(AQS)这个抽象类。AQS 是一个提供了基本同步机制的框架,其中包括了队列、状态值等。
- 可中断性。在底层,ReentrantLock 使用了与 LockSupport.park() 和 LockSupport.unpark() 相关的机制来实现可中断性。
- 设置超时时间。这是通过内部的 tryAcquireNanos 方法来实现的。
- 公平锁和非公平锁。公平锁的意思是按照线程等待时间长到短来顺序获取锁。
公平锁可以通过在创建 ReentrantLock 时传入 true 来设置,
Synchronized可重入锁原理
Synchronized可重入锁原理
线程请求锁,检查锁状态。
- 如果锁状态是0,代表没占用,使用CAS操作将线程ID换成自己的线程ID。
- 如果锁状态是不是0,代表锁被占用。如果是可重入锁,锁的线程ID是自己的就会重新获取锁,锁status+1。非重入锁,进入阻塞队列等。
释放锁时,
- 可重入锁,每一次退出方法,将会status-1,直至为0,最后释放该锁。
- 非重入锁的,线程退出方法,直接会释放该锁。
ReentrantLock锁可重入原理
和Synchronized锁类似。运用了一个锁计数器。