从Thread与Runable说开去
Thread
public
class Thread implements Runnable {}
-
JAVA虚拟机允许一个应用拥有多个并发执行的线程
-
每个线程都有一个优先级,高优先级的线程要优先于低优先级的执行
- 1-10的优先级 1最低
-
如果当一段代码创建了一个线程 那么这个线程的优先级和这个代码的线程的优先级相同,并且只有创建的线程是daemon线程 才会创建daemon线程
-
有两种创建线程的方法
-
一种是创建一个thead的子类
-
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime } }
启动线程
PrimeThread p = new PrimeThread(143); p.start();
-
-
一种是创建一个类 实现runnable
-
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime } }
启动线程
PrimeRun p = new PrimeRun(143); new Thread(p).start();
-
-
源码分析
//line 264 获取当前执行线程
public static native Thread currentThread();
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 线程组 被执行的目标 线程名字
private void init(ThreadGroup g, Runnable target, String name,
//新线程所需要栈的大小 0表示忽略
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
当调用 start()方法时,线程会开始执行,jvm会调用线程的run()方法,两个线程会同时执行(调用start()的线程 还有执行run的线程)
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//可以看出线程是操作系统给我们创建执行的
private native void start0();
Runnable
- Runnable需要被实现
- 如果只想重写run()而不是需要Thread 其他方法,那么只需要实现Runnable 接口
wait与sleep 分析
wait
Object的wait()方法都是套娃的 套的
public final native void wait(long timeout) throws InterruptedException;
这个方法,从下面可以看出
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
-
wait()会导致一个线程等待,直到另一个线程调用当前对象的notify()或者 notifyAll()
-
wait() 和 wait(0) 是等价的
-
当前线程必须拥有这个对象的锁,当使用了wait()会释放这个锁的拥有权,进行等待。直到另外一个线程通知了这个对象上等待的线程使用notify()或者 notifyAll()
-
这个方法应该运行在循环中
-
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }
-
-
这个方法应该被持有这个对象锁的线程调用
public final void wait() throws InterruptedException {
wait(0);
} -
所以wait()是本地方法,由os来维护线程的状态
Sleep
Threada方法中的sleep
public static native void sleep(long millis) throws InterruptedException;
- 让当前执行的线程进入睡眠状态
- 线程不会失去任何锁的所有权
总结
- 在调用wait方法时,线程必须要持有被调用对象的锁,当调用wait方法后,线程就会释放掉该对象的锁(monitor)
- 在调用Thread类的sleep方法时,线程是不会释放掉对象的锁的
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object) {
object.wait();
}
}
notify
public final native void notify();
- 会唤醒一个正在等待该对象监视器的线程。
- 如果由多个对象都在等待这个对象,其中的一个就会被选择唤醒(随机的)
- 被唤醒的线程无法继续执行,直到当前正在执行的线程让步了锁
- notify()应该被这个对象持有锁的所有者的线程调用
- 一个线程变成对象锁的所有者有以下方法:
- 执行对象的synchronized的实例方法
- 执行synchronized的语句块
- Class类型的对象执行类的标记为synchronized的静态方法
- 某一时刻只有一个线程持有对象的锁
notifyAll
public final native void notifyAll();
- 会唤醒在这个对象上的所有的线程
wait与notify和notifyAll方法的总结
- 当调用wait时,首先需要确保调用了wait方法的线程已经持有了对象的锁。
- 当调用wait后,该线程就会释放掉这个对象的锁,然后进入到等待状态(wait set)
- 当线程调用了wait后进入到等待状态时,它就可以等待其他线程调用相同对象的notify或notifyAll方法来使得自己被唤醒
- 一旦这个线程被其他线程唤醒后,该线程就会与其他线程一同开始竞争这个对象的锁(公平竞争);只有当该线程获取到了这个对象的锁后,线程才会继续往下执行
- 调用wait方法的代码片段需要放在一个synchronized块或是synchronized方法中,这样才可以确保线程在调用wait方法前
- 当调用对象的notify方法时,它会随机唤醒该对象等待集合(wait set)中的任意一个线程,当某个线程被唤醒后,它就会与其他线程一同竞争对象的锁
- 当调用对象的notifyAll方法时,它会唤醒该对象等待集合(wait set)中的所有线程,这些线程被唤醒后,又会开始竞争对象的锁。
- 在某一时刻,只有唯一一个线程可以拥有对象的锁。
public class a{
public synchronized void method1(){
}
public static synchronized void method1(){
}
}
Test test = new Test();
可以执行 一个是锁对象 一个是锁 CLass
代码
两个的时候用If没事 , 以上就要用while
public class MyObject {
private int counter;
public synchronized void increase() {
while (counter != 0) {
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
counter++;
System.out.println(counter);
notify();
}
public synchronized void decrease() {
while (counter == 0) {
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
counter--;
System.out.println(counter);
notify();
}
}
public class IncreaseThread extends Thread {
private MyObject myObject;
public IncreaseThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 0; i < 30; ++i) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
myObject.increase();
}
}
}
public class DecreaseThread extends Thread {
private MyObject myObject;
public DecreaseThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 0; i < 30; ++i) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
myObject.decrease();
}
}
}
public class Client {
public static void main(String[] args) {
MyObject myObject = new MyObject();
Thread increaseThread = new IncreaseThread(myObject);
Thread increaseThread2 = new IncreaseThread(myObject);
Thread decreaseThread = new DecreaseThread(myObject);
Thread decreaseThread2 = new DecreaseThread(myObject);
increaseThread.start();
increaseThread2.start();
decreaseThread.start();
decreaseThread2.start();
}
}
synchronized原理
public class MyThreadTest2 {
public static void main(String[] args) {
MyClass myClass = new MyClass();
MyClass myClass2 = new MyClass();
Thread t1 = new Thread1(myClass);
//Thread t2 = new Thread2(myClass); //hello world
Thread t2 = new Thread2(myClass2); //world hello
t1.start();
try {
Thread.sleep(700);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
class MyClass {
public static synchronized void hello () {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello");
}
public synchronized void world() {
System.out.println("world");
}
}
class Thread1 extends Thread {
private MyClass myClass;
public Thread1(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
myClass.hello();
}
}
class Thread2 extends Thread {
private MyClass myClass;
public Thread2(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
myClass.world();
}
}
- 如果一个有若干个synchronized方法,在某一时刻只能有唯一一个线程进入synchronized方法,其他线程想要进入synchronized方法也要等待
- 当一个对象里面有若干个synchronized方法的时候,某个线程想要访问这个方法里的synchronized,需要获取他的锁,这个锁是当前对象的锁,当前对象只有一把锁。
- 如果有线程访问两个对象的synchronized方法的时候,那么是不需要等待的,因为有2个锁
- 如果一个方法是static synchronized修饰的,一个线程访问的时候,获取的锁是当前对象的Class的锁,所有对象都锁,这个方法只能有一个对象被访问,没被static修饰的还是对象锁
synchronized 字节码
synchronized代码块
- 当我们使用synchronized关键字来修饰代码块时,字节码层面上是通过monitorenter与monitorexit指令来实现的锁的获取与释放动作。
- 当线程进入到monitorenter指令后,线程将会持有Monitor对象,退出monitorenter指令后,线程将会释放Monitor对象
自己写了异常 就只会有一个monitorexit
synchronized实例方法
- 对于synchronized关键字修饰方法来说,并没有出现monitorenter与monitorexit指令,而是出现了一个ACC_SYNCHRONIZED标志。
- JVM使用了ACC_SYNCHRONIZED访问标志来区分一个方法是否为同步方法;当方法被调用时,调用指令会检查该方法是否拥有ACC_SYNCHRONIZED标志,如果有,那么执行线程将会先持有方法所在对象的Monitor对象,然后再去执行方法体;在该方法执行期间,其他任何线程均无法再获取到这个Monitor对象,当线程执行完该方法后,它会释放掉这个Monitor对象。
static synchronized方法
底层实现和意义
-
JVM中的同步是基于进入与退出监视器对象(管程对象)(Monitor)来实现的,每个对象实例都会有一个Monitor对象,Monitor对象会和Java对象一同创建并销毁。Monitor对象是由C++来实现的。
-
当多个线程同时访问一段同步代码时,这些线程会被放到一个EntryList集合中,处于阻塞状态的线程都会被放到该列表当中。接下来,当线程获取到对象的Monitor时,Monitor是依赖于底层操作系统的mutex lock来实现互斥的,线程获取mutex成功,则会持有该mutex,这时其他线程就无法再获取到该mutex。
-
如果线程调用了wait方法,那么该线程就会释放掉所持有的mutex,并且该线程会进入到WaitSet集合(等待集合)中,等待下一次被其他线程调用notify/notifyAll唤醒。如果当前线程顺利执行完毕方法,那么它也会释放掉所持有的mutex。
-
总结一下:同步锁在这种实现方式当中,因为Monitor是依赖于底层的操作系统实现,这样就存在用户态与内核态之间的切换,所以会增加性能开销。
-
通过对象互斥锁的概念来保证共享数据操作的完整性。每个对象都对应于一个可称为『互斥锁』的标记,这个标记用于保证在任何时刻,只能有一个线程访问该对象。
-
那些处于EntryList(等待获取锁)与WaitSet(调用wait)中的线程均处于阻塞状态,阻塞操作是由操作系统来完成的,在linux下是通过pthread_mutex_lock函数实现的。线程被阻塞后便会进入到内核调度状态,这会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。
解决办法
解决上述问题的办法便是自旋(Spin)。其原理是:当发生对Monitor的争用时,若Owner能够在很短的时间内释放掉锁,则那些正在争用的线程就可以稍微等待一下(即所谓的自旋),在Owner线程释放锁之后,争用线程可能会立刻获取到锁,从而避免了系统阻塞。不过,当Owner运行的时间超过了临界值后,争用线程自旋一段时间后依然无法获取到锁,这时争用线程则会停止自旋而进入到阻塞状态。所以总体的思想是:先自旋,不成功再进行阻塞,尽量降低阻塞的可能性,这对那些执行时间很短的代码块来说有极大的性能提升。显然,自旋在多处理器(多核心)上才有意义。
互斥锁的属性:
- PTHREAD_MUTEX_TIMED_NP:这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将会形成一个等待队列,并且在解锁后按照优先级获取到锁。这种策略可以确保资源分配的公平性。
- PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁。允许一个线程对同一个锁成功获取多次,并通过unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新进行竞争。
- PTHREAD_MUTEX_ERRORCHECK_NP:检错锁。如果一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同,这样就保证了当不允许多次加锁时不会出现最简单情况下的死锁。
- PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,动作最简单的锁类型,仅仅等待解锁后重新竞争。
研究底层实现
WaitSet 和 EntryList
截取 ObjectMonitor部分代码 进行解析
class ObjectMonitor {
public:
enum {
OM_OK, // no error
OM_SYSTEM_ERROR, // operating system error
OM_ILLEGAL_MONITOR_STATE, // IllegalMonitorStateException
OM_INTERRUPTED, // Thread.interrupt()
OM_TIMED_OUT // Object.wait() timed out
};
// initialize the monitor, exception the semaphore, all other fields
// are simple integers or pointers
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
Wait Notify NotifyAll
ObjectWaiter node(Self); //将当前执行的线程 变成ObjectWait线程
node.TState = ObjectWaiter::TS_WAIT ; //设置成等待状态
AddWaiter (&node) ;
//把当前线程放到WaitSet中(双向链表实现)
inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {
assert(node != NULL, "should not dequeue NULL node");
assert(node->_prev == NULL, "node already in list");
assert(node->_next == NULL, "node already in list");
// put node at end of queue (circular doubly linked list)
if (_WaitSet == NULL) {
_WaitSet = node;
node->_prev = node;
node->_next = node;
} else {
ObjectWaiter* head = _WaitSet ;
ObjectWaiter* tail = head->_prev;
assert(tail->_next == head, "invariant check");
tail->_next = node;
head->_prev = node;
node->_next = head;
node->_prev = tail;
}
}
//释放monitor
exit (true, Self) ; // exit the monitor
norify
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) { //如果没有就直接返回
TEVENT (Empty-Notify) ;
return ;
}
ObjectWaiter * iterator = DequeueWaiter() ;
//获取第一个对象
inline ObjectWaiter* ObjectMonitor::DequeueWaiter() {
// dequeue the very first waiter
ObjectWaiter* waiter = _WaitSet;
if (waiter) {
DequeueSpecificWaiter(waiter);
}
return waiter;
}
//不同的调度策略
if (Policy != 4) {
iterator->TState = ObjectWaiter::TS_ENTER ;
}
ObjectWaiter * List = _EntryList ;
if (List != NULL) {
assert (List->_prev == NULL, "invariant") ;
assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
assert (List != iterator, "invariant") ;
}
if (Policy == 0) { // prepend to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) { // append to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
// CONSIDER: finding the tail currently requires a linear-time walk of
// the EntryList. We can make tail access constant-time by converting to
// a CDLL instead of using our current DLL.
ObjectWaiter * Tail ;
for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
}
} else
if (Policy == 2) { // prepend to cxq
// prepend to cxq
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Front = _cxq ;
iterator->_next = Front ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
break ;
}
}
} else
if (Policy == 3) { // append to cxq
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Tail ;
Tail = _cxq ;
if (Tail == NULL) {
iterator->_next = NULL ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
break ;
}
} else {
while (Tail->_next != NULL) Tail = Tail->_next ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
break ;
}
} else {
ParkEvent * ev = iterator->_event ;
iterator->TState = ObjectWaiter::TS_RUN ;
OrderAccess::fence() ;
ev->unpark() ; //唤醒阻塞的线程 从waitSet移动到 entrylist(改变指针走向)
}
锁升级
-
在JDK 1.5之前,我们若想实现线程同步,只能通过synchronized关键字这一种方式来达成;底层,Java也是通过synchronized关键字来做到数据的原子性维护的;synchronized关键字是JVM实现的一种内置锁,从底层角度来说,这种锁的获取与释放都是由JVM帮助我们隐式实现的。
-
从JDK 1.5开始,并发包引入了Lock锁,Lock同步锁是基于Java来实现的,因此锁的获取与释放都是通过Java代码来实现与控制的;然而,synchronized是基于底层操作系统的Mutex Lock来实现的,每次对锁的获取与释放动作都会带来用户态与内核态之间的切换,这种切换会极大地增加系统的负担;在并发量较高时,也就是说锁的竞争比较激烈时,synchronized锁在性能上的表现就非常差。
-
从JDK 1.6开始,synchronized锁的实现发生了很大的变化;JVM引入了相应的优化手段来提升synchronized锁的性能,这种提升涉及到偏向锁、轻量级锁及重量级锁等,从而减少锁的竞争所带来的用户态与内核态之间的切换;这种锁的优化实际上是通过Java对象头中的一些标志位来去实现的;对于锁的访问与改变,实际上都与Java对象头息息相关。
从JDK 1.6开始,对象实例在堆当中会被划分为三个组成部分:对象头、实例数据与对齐填充。
对象头主要也是由3块内容来构成:
- Mark Word
- 指向类的指针
- 数组长度
其中Mark Word(它记录了对象、锁及垃圾回收相关的信息,在64位的JVM中,其长度也是64bit)的位信息包括了如下组成部分:
- 无锁标记
- 偏向锁标记
- 轻量级锁标记
- 重量级锁标记
- GC标记
对于synchronized锁来说,锁的升级主要都是通过Mark Word中的锁标志位与是否是偏向锁标志位来达成的;synchronized关键字所对应的锁都是先从偏向锁
开始,随着锁竞争的不断升级,逐步演化至轻量级锁,最后则变成了重量级锁。
对于锁的演化来说,它会经历如下阶段:
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
偏向锁:
针对于一个线程来说的,它的主要作用就是优化同一个线程多次获取一个锁的情况;如果一个synchronized方法被一个线程访问,那么这个方法所在的对象就会在其Mark Word中将偏向锁进行标记,同时还会有一个字段来存储该线程的ID;当这个线程再次访问同一个synchronized方法时,它会检查这个对象的Mark Word的偏向锁标记以及是否指向了其线程ID,如果是的话,那么该线程就无需再去进入管程(Monitor)了,而是直接进入到该方法体中。
如果是另外一个线程访问这个synchronized方法,那么实际情况会如何呢?
偏向锁会被取消掉。
轻量级锁:
若第一个线程已经获取到了当前对象的锁,这时第二个线程又开始尝试争抢该对象的锁,由于该对象的锁已经被第一个线程获取到,因此它是偏向锁,而第二个线程在争抢时,会发现该对象头中的Mark Word已经是偏向锁,但里面存储的线程ID并不是自己(是第一个线程),那么它会进行CAS(Compare and Swap),从而获取到锁,这里面存在两种情况:
- 获取锁成功:那么它会直接将Mark Word中的线程ID由第一个线程变成自己(偏向锁标记位保持不变),这样该对象依然会保持偏向锁的状态。
- 获取锁失败:则表示这时可能会有多个线程同时在尝试争抢该对象的锁,那么这时偏向锁就会进行升级,升级为轻量级锁
自旋锁:
若自旋失败(依然无法获取到锁),那么锁就会转化为重量级锁,在这种情况下,无法获取到锁的线程都会进入到Monitor(即内核态)
自旋最大的一个特点就是避免了线程从用户态进入到内核态。
重量级锁:
线程最终从用户态进入到了内核态。
锁粗化 和锁消除
编译器对于锁的优化措施:
锁消除
JIT编译器(Just In Time编译器)可以在动态编译同步代码时,使用一种叫做逃逸分析的技术,来通过该项技术判别程序中所使用的锁对象是否只被一个线程所使用,而没有散布到其他线程当中;如果情况就是这样的话,那么JIT编译器在编译这个同步代码时就不会生成synchronized关键字所标识的锁的申请与释放机器码,从而消除了锁的使用流程。
public class MyTest4 {
// private Object object = new Object();
public void method() {
Object object = new Object();
synchronized (object) {
System.out.println("hello world");
}
}
}
锁粗化
JIT编译器在执行动态编译时,若发现前后相邻的synchronized块使用的是同一个锁对象,那么它就会把这几个synchronized块给合并为一个较大的同步块,这样做的好处在于线程在执行这些代码时,就无需频繁申请与释放锁了,从而达到申请与释放锁一次,就可以执行完全部的同步代码块,从而提升了性能
public class MyTest5 {
private Object object = new Object();
public void method() {
synchronized (object) {
System.out.println("hello world");
}
synchronized (object) {
System.out.println("welcome");
}
synchronized (object) {
System.out.println("person");
}
}
}
锁机制(死锁)
- 死锁:线程1等待线程2互斥持有的资源,而线程2也在等待线程1互斥持有的资源,两个线程都无法继续执行
- 活锁:线程持续重试一个总是失败的操作,导致无法继续执行
- 饿死:线程一直被调度器延迟访问其赖以执行的资源,也许是调度器先于低优先级的线程而执行高优先级的 线程,同时总是会有一个高优先级 的线程可以执行,饿死也叫做无限延迟
死锁
public class MyTest6 {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void myMethod1() {
synchronized (lock1) {
synchronized (lock2) {
System.out.println("myMethod1 invoked");
}
}
}
public void myMethod2() {
synchronized (lock2) {
synchronized (lock1) {
System.out.println("myMethod2 invoked");
}
}
}
public static void main(String[] args) {
MyTest6 myTest6 = new MyTest6();
Runnable runnable1 = () -> {
while (true) {
myTest6.myMethod1();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
};
Thread thread1 = new Thread(runnable1, "myThread1");
Runnable runnable2 = () -> {
while (true) {
myTest6.myMethod2();
try {
Thread.sleep(220);
} catch (InterruptedException ex) {
}
}
};
Thread thread2 = new Thread(runnable2, "myThread2");
thread1.start();
thread2.start();
}
}
Lock
-
一般情况下,锁会对于共享资源采取排他操作,一般需要获取锁才能操作。但是有些锁可以对共享锁采取并发操作,比如:ReadWriteLock.
-
synchronized 锁的获取 是 A B 先释放B 再释放A 需要是相同的作用域
-
Lock锁需要先获取A再获取B 先释放A 再获取C 在释放B 在获取D ,Lock可以在不同作用域获取和释放
-
public interface Lock { /* 获取锁 如果获取不到锁,就会等待(睡眠) 可以用于检测锁的错误使用 */ void lock(); /* 如果线程没用中断就获取锁 如果可以获取就立马返回 如果获取不到就进行睡眠,直到下面两个发生 1.Lock被当前线程获取 2.其他线程中断当前线程(允许中断) */ void lockInterruptibly() throws InterruptedException; /* 当调用的时候可以获取的时候才能获取,并且立马返回 true if(l.tryLock()){ try{ }finally{ l.unlock(); } }else{ //没有获取到锁 } */ boolean tryLock(); /* 在给定时间内获取到锁的情况下(没有中断) 返回true, 反之进入睡眠状态,直到下面三种情况 1.当前线程在等待时获取到锁 2.其他线程中断了线程(允许中断) 3.指定的等待时间过去 如果锁被获取了返回true 如果中断了当前线程,就会返回false 超过了指定等待时间 返回false */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /* 返回一个新的condition实例,绑定到当前线程上(当前线程必须持有lock) 调用Condition.await()会自动释放锁(等待之前并且wait方法返回之前 重新获取锁) */ Condition newCondition(); }
-
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
public class MyTest1 {
private Lock lock = new ReentrantLock(); // 可重入锁
public void myMethod1() {
try {
lock.lock();
System.out.println("myMethod1 invoked");
} finally {
lock.unlock(); //全是myMethod1 invoked 因为是可重入锁,所以可以一直获取
}
}
public void myMethod2() {
try {
lock.lock();
System.out.println("myMethod2 invoked");
} finally {
lock.unlock();
}
// boolean result = false; 这样程序是可以走的 就算上面睡眠没有unlock
//
// try {
// result = lock.tryLock(800, TimeUnit.MILLISECONDS);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// if (result) {
// System.out.println("get the lock");
// } else {
// System.out.println("can't get the lock");
// }
}
public static void main(String[] args) {
MyTest1 myTest1 = new MyTest1();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; ++i) {
myTest1.myMethod1();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; ++i) {
myTest1.myMethod2();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
}
}
关于Lock与synchronized关键字在锁的处理上的重要差别
- 锁的获取方式:前者是通过程序代码的方式由开发者手工获取,后者是通过JVM来获取(无需开发者干预)
- 具体实现方式:前者是通过Java代码的方式来实现,后者是通过JVM底层来实现 (无需开发者关注)
- 锁的释放方式:前者务必通过unlock()方法在finally块中手工释放,后者是通过JVM来释放(无需开发者关注)
- 锁的具体类型:前者提供了多种,如公平锁、非公平锁,后者与前者均提供了可重入锁
Condition
传统上,我们可以通过synchronized关键字 + wait + notify/notifyAll 来实现多个线程之间的协调与通信,整个过程都是由JVM来帮助
我们实现的;开发者无需(也是无法)了解底层的实现细节
类似于Object
从JDK 5开始,并发包提供了Lock, Condition(await与signal/signalAll)来实现多个线程之间的协调与通信,整个过程都是由开发者来
控制的,而且相比于传统方式,更加灵活,功能也更加强大
Thread.sleep与await(或是Object的wait方法)的本质区别:sleep方法本质上不会释放锁,而await会释放锁,并且在signal后,还需要
重新获得锁才能继续执行(该行为与Object的wait方法完全一致)
- Condition让一个对象可以拥有多个等待队列
- 在
Lock
取代synchronized
方法和语句的使用,一个Condition
取代对象监视器的使用方法。 - Lock newCondition() 中的condition一定是和Lock关联的
public interface Condition {
/*
让当前线程等待,直到另一个线程signall或者被中断
与Condition相关的Lock会自动释放,当前线程无法调度,直到
1.其他某个线程调用此Condition的signal()方法和当前线程会被选为被唤醒的线程
2.其他某个线程调用此Condition的signalAll()发法
3.其他某个线程[interrupts]当前线程,线程挂起中断或支持
4.虚假唤醒
在所有情况下,在此方法返回之前,当前线程必须重新获得与此条件相关联的锁。当线程返回时,它保证保持这个锁,如果当前线程:
1. 在该方法的入口上有它的中断状态;或
2。 是[interrupted]等待和线程挂起中断的支持,
然后 [`InterruptedException]投入和当前线程的中断状态被清除。它没有被指定,在第一种情况下,是否中断测试之前发生的锁被释放。
*/
void await() throws InterruptedException;
/*
不可中断
导致当前线程等待,自动释放锁进入睡眠状态,除非
1.sign
2.signall
3.虚假唤醒
*/
void awaitUninterruptibly();
/*
当前线程等待,释放锁 直到
1.signal
2.signalAll
3.中断
4.指定等待时间过去
5.虚假唤醒
会返回剩余时间(<=0代表超时)
*/
long awaitNanos(long nanosTimeout) throws InterruptedException;
/*
*/
boolean await(long time, TimeUnit unit) throws InterruptedException;
/*
*/
boolean awaitUntil(Date deadline) throws InterruptedException;
/*
如果有多个等待根据某种策略选择一个唤醒
*/
void signal();
/*
唤醒所有等待线程
*/
void signalAll();
}
public class MyTest2 {
public static void main(String[] args) {
BoundedContainer boundedContainer = new BoundedContainer();
IntStream.range(0, 10).forEach(i -> new Thread(() -> {
try {
boundedContainer.put("hello");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start());
IntStream.range(0, 10).forEach(i -> new Thread(() -> {
try {
boundedContainer.take();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start());
}
}
class BoundedContainer {
private String[] elements = new String[10];
private Lock lock = new ReentrantLock();
private Condition notEmptyCondition = lock.newCondition();
private Condition notFullCondition = lock.newCondition();
private int elementCount; // elements数组中已有的元素数量
private int putIndex;
private int takeIndex;
public void put(String element) throws InterruptedException {
this.lock.lock();
try {
while (this.elementCount == this.elements.length) {
notFullCondition.await();
}
elements[putIndex] = element;
if (++putIndex == this.elements.length) {
putIndex = 0;
}
++elementCount;
System.out.println("put method: " + Arrays.toString(elements));
notEmptyCondition.signal();
} finally {
this.lock.unlock();
}
}
public String take() throws InterruptedException {
this.lock.lock();
try {
while (0 == this.elementCount) {
notEmptyCondition.await();
}
String element = elements[takeIndex];
elements[takeIndex] = null;
if (++takeIndex == this.elements.length) {
takeIndex = 0;
}
--elementCount;
System.out.println("take method: " + Arrays.toString(elements));
notFullCondition.signal();
return element;
} finally {
this.lock.unlock();
}
}
}
await()
public final void await() throws InterruptedException {
// 检测线程中断状态
if (Thread.interrupted())
throw new InterruptedException();
// 将当前线程包装为Node节点加入等待队列
Node node = addConditionWaiter();
// 释放同步状态,也就是释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 检测该节点是否在同步队中,如果不在,则说明该线程还不具备竞争锁的资格,则继续等待
while (!isOnSyncQueue(node)) {
// 挂起线程
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 竞争同步状态
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 清理条件队列中的不是在等待条件的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal
//将等待线程中,等待时间最长的移除 从Condtion的等待队列,到拥有锁的等待队列中
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//第一个等待者
Node first = firstWaiter;
if (first != null)
//移动
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
//将Condition的node移动到同步队列中
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
volatile关键字
- private volatile int count;
- volatile关键字主要有三方面作用:
- 1.实现long/double类型变量的原子操作
- 2.防止指令重排序
- 3.实现变量的可见性
long a = 1L -》 64位 的写入顺序是先写入低32位 在写入高32位,单线程操作没事 多线程操作可能出错
- volatile double a = 1.0
- 当使用volatile修饰变量时,应用就不会从寄存器中获取该变量的值,而是从内存(高速缓存)中获取。
volatile与锁类似的地方有两点:
- 确保变量的内存可见性
- 防止指令重排序
-
volatile可以确保对变量写操作的原子性,但不具备排他性
-
另外的重要一点在于:使用锁可能会导致线程的上下文切换(内核态与用户态之间的切换),但使用volatile并不会出现这种情况
volatile int a = b + 2;
没用
volatile int a = a++;
不行
volatile int count = 1;
可以
volatile boolean flag = false;
可以
如果要实现volatile写操作的原子性,那么在等号右侧的赋值变量中就不能出现被多线程所共享的变量,哪怕这个变量也是个volatile也不可以。
volatile Date date = new Date();
单线程操作可以 多线程操作不行 new Date(); 实现步骤:
1.对创建对象 2.返回引用 不是原子操作 volatile只能保证 引用返回给date是原子操作
防止指令重排序与实现变量的可见性都是通过一种手段来实现的:内存屏障(memory barrier)
int a = 1;
String s = "hello";
内存屏障 (Release Barrier,释放屏障) //把上面的发布 执行volatile之前 其他线程可以看见前面所有的修改,对共享代码所有的顺序与原来的顺序保持一致
volatile boolean v = false; // 写入操作
内存屏障 (Store Barrier,存储屏障)
- Release Barrier:防止下面的volatile与上面的所有操作的指令重排序。
- Store Barrier:重要作用是刷新处理器缓存,结果是可以确保该存储屏障之前一切的操作所生成的结果对于其他处理器来说都可见。
内存屏障(Load Barrier,加载屏障)
boolean v1 = v;
内存屏障(Acquire Barrier,获取屏障)
int a = 1;
String s = “hello”;
- Load Barrier:可以刷新处理器缓存,同步其他处理器对该volatile变量的修改结果。
- Acquire Barrier:可以防止上面的volatile读取操作与下面的所有操作语句的指令重排序。
对于volatile关键字变量的读写操作,本质上都是通过内存屏障来执行的。
内存屏障兼具了两方面能力:1. 防止指令重排序, 2. 实现变量内存的可见性。
- 对于读取操作来说,volatile可以确保该操作与其后续的所有读写操作都不会进行指令重排序。
- 对于修改操作来说,volatile可以确保该操作与其上面的所有读写操作都不会进行指令重排序。
volatile与锁的一些比较
锁同样具备变量内存可见性与防止指令重排序的功能。
monitorenter
内存屏障(Acquire Barrier,获取屏障)
…
内存屏障 (Release Barrier,释放屏障)
monitorexit
JMM& happen-before
- 变量的原子性问题。
- 变量的可见性问题
- 变量修改的时序性问题。
JMM内存模型
java内存模型是共享内存的并发模型,线程之间主要通过读-写共享变量来完成隐式通信。java中的共享变量是存储在内存中的,多个线程由其工作内存,其工作方式是将共享内存中的变量拿出来放在工作内存,操作完成后,再将最新的变量放回共享变量,这时其他的线程就可以获取到最新的共享变量。
从横向去看看,线程A和线程B就好像通过共享变量在进行隐式通信。这其中有很有意思的问题,如果线程A更新后数据并没有及时写回到主存,而此时线程B读到的是过期的数据,这就出现了 “脏读” 现象。
为避免脏读,可以通过同步机制(控制不同线程间操作发生的相对顺序)来解决或者通过volatile关键字使得每次volatile变量都能够强制刷新到主存,从而对每个线程都是可见的。
什么是happen-before
JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。
具体的定义为:
1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
2)两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。
happen-before重要规则:
- 顺序执行规则(限定在单个线程上的):该线程的每个动作都happen-before它的后面的动作。(与指令重排序不冲突)
- 隐式锁(monitor)规则(同一把锁):unlock happen-before lock,之前的线程对于同步代码块的所有执行结果对于后续获取锁的线程来说都是可见的。
- volatile读写规则:对于一个volatile变量的写操作一定会happen-before后续对该变量的读操作。
- 多线程的启动规则:Thread对象的start方法happen-before该线程run方法中的任何一个动作,包括在其中启动的任何子线程(父线程的所有操作 子线程都可见)。
- 多线程的终止规则:一个线程启动了一个子线程,并且调用了子线程的join方法等待其结束,那么当子线程结束后,父线程的接下来的所有操作
都可以看到子线程run方法中的执行结果。 - 线程的中断规则:可以调用interrupt方法来中断线程,这个调用happen-before对该线程中断的检查(isInterrupted)。
public class MyTest4 {
//不具备happen -before
private int x;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
CountDownLatch
- 其他任务完成后 才执行
- 一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
public CountDownLatch(int count) { }; //参数count为计数值
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
public class MyTest1 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(3); //执行的子任务的数量
IntStream.range(0, 3).forEach(i -> new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("hello");
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
countDownLatch.countDown(); //以原子的形式 -1 减到0 就不会变化了 不然一直等待
}
}).start());
System.out.println("启动子线程完毕");
try {
countDownLatch.await(); // 》0就等待
countDownLatch.await(200,TimeUnit.MILLISECONDS); //超过时间就往下执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行完毕");
}
线程在countDown()之后,会继续执行自己的任务,而CyclicBarrier会在所有线程任务结束之后,才会进行后续任务
CyclicBarrier
若干个线程到达一个点之后 然后共同执行
关于CyclicBarrier的底层执行流程
- 初始化CyclicBarrier中的各种成员变量,包括parties、count以及Runnable(可选)
- 当调用await方法时,底层会先检查计数器是否已经归零,如果是的话,那么就首先执行可选的Runnable,接下来开始下一个generation;
- 在下一个分代中,将会重置count值为parties,并且创建新的Generation实例。
- 同时会调用Condition的signalAll方法,唤醒所有在屏障前面等待的线程,让其开始继续执行。
- 如果计数器没有归零,那么当前的调用线程将会通过Condition的await方法,在屏障前进行等待。
- 以上所有执行流程均在lock锁的控制范围内,不会出现并发情况。
public class MyTest2 {
public static void main(String[] args) {
//可以重复使用
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
System.out.println("hello world");
});
/** 最后一个线程到达屏障会执行一次
* public CyclicBarrier(int parties, Runnable barrierAction) {
* if (parties <= 0) throw new IllegalArgumentException();
* this.parties = parties;
* this.count = parties;
* this.barrierCommand = barrierAction;
* }
*/
for (int i = 0; i < 2; ++i) {
for (int n = 0; n < 3; ++n) {
new Thread(() -> {
try {
Thread.sleep((long) (Math.random() * 2000));
int randomInt = new Random().nextInt(500);
System.out.println("hello " + randomInt);
cyclicBarrier.await(); //如果没到3个 就等待,到了 就通知其他线程 一起执行
// public int await(long timeout, TimeUnit unit) //超时了 会触发异常
//
System.out.println("world " + randomInt);
} catch (Exception ex) {
ex.printStackTrace();
}
}).start();
}
}
}
}
CAS
- synchronized关键字与Lock等锁机制都是悲观锁:无论做何种操作,首先都需要先上锁,接下来再去执行后续操作,从而确保了
接下来的所有操作都是由当前这个线程来执行的。 - 乐观锁:线程在操作之前不会做任何预先的处理,而是直接去执行;当在最后执行变量更新的时候,当前线程需要有一种机制来确保
当前被操作的变量是没有被其他线程修改的;CAS是乐观锁的一种极为重要的实现方式。
CAS (Compare And Swap)
比较与交换:这是一个不断循环的过程,一直到变量值被修改成功为止。CAS本身是由硬件指令来提供支持的,换句话说,硬件中是通过一个
原子指令来实现比较与交换的;因此,CAS可以确保变量操作的原子性的。
public class MyTest1 {
private int count;
//synchronized 不修饰 会导致 可能读不到最新数据 synchronized只能单线程串行
public synchronized int getCount() {
return count;
}
/*
读取 -> 修改 -> 写入:这三个操作并非原子操作
*/
//synchronized修饰了 数据一修改 会立马刷新
public synchronized void increaseCount() {
++this.count;
}
}
Future
- 代表了异步执行的结果,提供多种方法去检查异步计算是否完成
- get方法获取结果,如果还没计算结束 GET会进行等待
public interface Future<V> {
/*
取消任务执行(不能再次执行)
*/
boolean cancel(boolean mayInterruptIfRunning);
/*
任务是否取消了
*/
boolean isCancelled();
/*
任务是否完成
*/
boolean isDone();
/*
返回Futrue异步任务的执行结果,如果任务还没计算完,会进入阻塞状态
*/
V get() throws InterruptedException, ExecutionException;
/*
如果在指定时间内没获取到值,抛出异常
指定时间内 就返回值
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
|Implement
RunnableFuture
|extends
Runnable, Future
FutureTask
最终任务都是转换成Callable
//需要返回结果
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//不需要返回结果 使用这个
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); //返回最终的结果
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
public class MyTest1 {
public static void main(String[] args) {
Callable<Integer> callable = () -> {
System.out.println("pre execution");
Thread.sleep(5000);
int randomNumber = new Random().nextInt(500);
//不影响子线程
System.out.println("post execution");
return randomNumber;
};
FutureTask<Integer> futureTask = new FutureTask(callable);
new Thread(futureTask).start();
System.out.println("thread has started");
try {
Thread.sleep(2000);
//超时 主线程报异常
System.out.println(futureTask.get(1, TimeUnit.MILLISECONDS));
} catch (Exception e) {
e.printStackTrace();
}
}
}
CompletableFuture
一个任务可能分成多个阶段,前一个阶段完成触发后面一个阶段,每一个阶段就是CompletionStage
//具备Futrue的功能,提供了异步的执行 和异步的获取结果
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
}
public class MyTest2 {
public static void main(String[] args) {
/* //返回一个新的CompletableFuture 可以被一个运行的任务异步完成
String result = CompletableFuture.supplyAsync(() -> "hello")
//然后在去应用一个异步任务
//需要返回结果使用这个
.thenApplyAsync(value -> value + " world").join();
System.out.println(result);//对结果进行变换 hellow world
System.out.println("========");
//不需要返回任务
CompletableFuture.supplyAsync(() -> "hello").thenAccept(value -> System.out.println("welcome " + value));
System.out.println("========");*/
// 对于stage的合并操作
/* String result2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "world";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result2);
System.out.println("========");*/
//异步
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finished");
});
//当任务完成就去执行回调 不阻塞
completableFuture.whenComplete((t, action) -> System.out.println("执行完成!"));
System.out.println("主线程执行完毕");
try {
TimeUnit.MILLISECONDS.sleep(7000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadLocal
本质上,ThreadLocal是通过空间来换取时间,从而实现每个线程当中都会有一个变量的副本,这样每个线程就都会操作该副本,从而完全规避了多线程的并发问题。
Java中存在四种类型的引用:
- 强引用(strong)
- 软引用(soft)
- 弱引用(weak)
- 虚引用(phantom)
原理:
ThreadLocal 里面维护了一个ThreadLocalMap(继承了WeakReference防止内存泄漏(但是如果写的不对还是有可能导致内存泄漏)),以ThreadLocal为键,要放入的值为value
public class Test{
//保证Test所有实例共享
private static final ThreadLocal<String> tl = new ThreadLocal();
}
//一旦不要了 这样使用
try {
...
...
...
} finally {
tl.remove();
}
public class MyTest3 {
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal();
threadLocal.set("hello world");
System.out.println(threadLocal.get());
threadLocal.set("welcome");
System.out.println(threadLocal.get());
}
}
AQS
- 提供一种框架来实现阻塞,依靠FIFO的等待队列
- 依赖原子性的int值 来表示状态
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
}
public class ConditionObject implements Condition, java.io.Serializable {
}
//AQS里面的FIFO队列
static final class Node {
//共享锁
static final Node SHARED = new Node();
//排他锁
static final Node EXCLUSIVE = null;
//线程取消
static final int CANCELLED = 1;
static final int SIGNAL = -1;
//某种情况发生
static final int CONDITION = -2;
//使用Signal SignalAll唤醒其他线程
static final int PROPAGATE = -3;
...
volatile Node prev;
volatile Node next;
//同步状态
//排他锁中标识线程中可重入的次数 lock+1 unlock -1
//读写锁中 state拆成两块 高16位:读线程的数量 低16位:写线程的数量
private volatile int state;
}
public Condition newCondition() {
return sync.newCondition();
}
//newCondition 就创建一个ConditionObject对象 并且互相没有关系
final ConditionObject newCondition() {
return new ConditionObject();
}
ReentrantLock
对于ReentrantLock来说,其执行逻辑如下所示:
- 尝试获取对象的锁,如果获取不到(意味着已经有其他线程持有了锁,并且尚未释放),那么它就会进入到AQS的阻塞队列当中。
- 如果获取到,那么根据锁是公平锁还是非公平锁来进行不同的处理
2.1 如果是公平锁,那么线程会直接放置到AQS阻塞队列的末尾
2.2 如果是非公平锁,那么线程会首先尝试进行CAS计算,如果成功,则直接获取到锁;(不传参默认)
如果失败,则与公平锁的处理方式一致,被放到阻塞队列末尾 - 当锁被释放时(调用了unlock方法),那么底层会调用release方法对state成员变量值进行减一操作,如果减一后,state值不为0,那么release操作就执行完毕;如果减一操作后,state值为0,则调用LockSupport的unpark方法唤醒该线程后的等待队列中的第一个后继线程(pthread_mutex_unlock),将其唤醒,使之能够获取到对象的锁(release时,对于公平锁与非公平锁的处理逻辑是一致的);之所以调用release方法后state值可能不为零,原因在于ReentrantLock是可重入锁,表示线程可以多次调用lock方法, 导致每调用一次,state值都会加一。
对于ReentrantLock来说,所谓的上锁,本质上就是对AQS中的state成员变量的操作:对该成员变量+1,表示上锁;对该成员变量-1,表示释放锁。
公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
//尝试获取锁
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//判断线程获取锁的个数 调用1次 +1
int c = getState();
//没有线程获取到锁
if (c == 0) {
//判断队列有没有等待的线程
if (!hasQueuedPredecessors() && //没有等待的线程
compareAndSetState(0, acquires)) {//把0改成了1 已经获取到锁
setExclusiveOwnerThread(current);//设置排他锁
return true; //设置成功
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
不公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// 0-》1
if (compareAndSetState(0, 1))
//设置排他锁
setExclusiveOwnerThread(Thread.currentThread());
else
//在排他的情况下获取锁
acquire(1);
}
//对锁插队 不能插队就排队
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//不排队直接获取锁 区别:没有判断阻塞队列有没有元素
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//都是可重入锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
unlock
//可重入锁 lock+1 unlock-1
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//传入1 用来-1 false代表还有线程持有锁 true代表锁都释放了
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后面一个锁
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
// state-1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//没有线程有锁了
if (c == 0) {
free = true;
//没有线程持有排他锁
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
ReentrantReadWriteLock
读锁:
- 在获取读锁时,会尝试判断当前对象是否拥有了写锁,如果已经拥有,则直接失败。
- 如果没有写锁,就表示当前对象没有排他锁,则当前线程会尝试给对象加锁
- 如果当前线程已经持有了该对象的锁,那么直接将读锁数量加1
写锁:
- 在获取写锁时,会尝试判断当前对象是否拥有了锁(读锁与写锁),如果已经拥有且持有的线程并非当前线程,直接失败。
- 如果当前对象没有被加锁,那么写锁就会为为当前对象上锁,并且将写锁的个数加1.
- 将当前对象的排他锁线程持有者设为自己
read.lock的核心代码
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
//返回低16位 写锁的数量
if (exclusiveCount(c) != 0 &&
//当前线程不持有锁
getExclusiveOwnerThread() != current)
return -1;
//当前没有其他线程持有写锁
//读取高16位
int r = sharedCount(c);
//判断队列有没有其他线程等待
if (!readerShouldBlock() &&
//锁的数量没有超过上限
r < MAX_COUNT &&
//将原来值 加上
compareAndSetState(c, c + SHARED_UNIT)) {
//之前没有读锁
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
//有其他线程加读锁
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
//完善读锁
return fullTryAcquireShared(current);
}
write.lock的核心实现(写锁互斥)
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
//获取写锁数量
int w = exclusiveCount(c);
//写锁已经被线程持有
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire 我已经给对象加上写锁了 再次+1
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
//有其他写线程或者读线程 进行竞争,不能获取锁
!compareAndSetState(c, c + acquires))
return false;
//成功获取写锁
setExclusiveOwnerThread(current);
return true;
}
readLook.unLock()
/*
如果读锁现在时0 Lock对于写锁就是可用的
*/
public void unlock() {
sync.releaseShared(1);
}
/*
*/
public final boolean releaseShared(int arg) {
//true 代表取消arg个线程的阻塞 释放共享锁 高16位 读锁 写锁都没了
if (tryReleaseShared(arg)) {
//通知后面线程唤醒
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int unused) {
//获取当前执行的线程
Thread current = Thread.currentThread();
//第一个读的线程对象()
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
//释放锁 第一个线程 持有锁的数量
if (firstReaderHoldCount == 1)
firstReader = null;
else
//重入锁
firstReaderHoldCount--;
} else { //不是第一个读的线程
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
//线程持有锁的读的数量
int count = rh.count;
if (count <= 1) {
//去除不需要的键值对
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
//- 高16位
int nextc = c - SHARED_UNIT;
//置换
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
//对读锁 无关,写锁有机会获得
return nextc == 0;
}
}
writeLook.unLock()
//排他锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//true 没有锁了 false 还有线程持有排他锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//后面的线程有机会获得锁
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//isHeldExclusively:true代表排他锁 不是排他锁 直接退出
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//state-1 释放了1个
int nextc = getState() - releases;
//低16位 排他锁数量 ==0 true:说明没有线程持有锁
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
关于AQS与synchronized关键字之间的关系:
-
synchronized关键字在底层的C++实现中,存在两个重要的数据结构(集合):WaitSet, EntryList
-
WaitSet中存放的是调用了Object的wait方法的线程对象(被封装成了C++的Node对象)
-
EntryList中存放的是陷入到阻塞状态、需要获取monitor的那些线程对象
-
当一个线程被notify后,它就会从WaitSet中移动到EntryList中。
-
进入到EntryList后,该线程依然需要与其他线程争抢monitor对象
-
如果争抢到,就表示该线程获取到了对象的锁,它就可以以排他方式执行对应的同步代码。
-
AQS中存在两种队列,分别是Condition对象上的条件队列,以及AQS本身的阻塞队列
-
这两个队列中的每一个对象都是Node实例(里面封装了线程对象)
-
当位于Condition条件队列中的线程被其他线程signal后,该线程就会从条件队列中移动到AQS的阻塞队列中。
-
位于AQS阻塞队列中的Node对象本质上都是由一个双向链表来构成的。
-
在获取AQS锁时,这些进入到阻塞队列中的线程会按照在队列中的排序先后尝试获取。
-
当AQS阻塞队列中的线程获取到锁后,就表示该线程已经可以正常执行了
-
陷入到阻塞状态的线程,依然需要进入到操作系统的内核态,进入阻塞(park方法实现)
线程池
Executor
- 可以执行提交的
runnable
的任务,接口将任务的提交 和 每个 任务执行的机制解耦
public interface Executor {
void execute(Runnable command);
}
ExecutorService
- 提供了方法生成
Future
用来追踪多个异步任务的执行情况 - 线程池用完要关闭
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
}
ThreadPoolExecutor
-
AbstractExecutorService线程池实现的基础,提供了一些默认实现
-
提供改进的性能在执行大规模异步任务
-
提供边界处理,统计上的任务
-
在线程池中,最好将偏向锁的标记关闭。
-
对于线程池来说,存在两个状态需要维护:
- 线程池本身的状态:ctl的高3位来表示
- 线程池中所运行着的线程的数量:ctl的其余29位来表示
- 线程池一共存在5种状态:
- RUNNING:线程池可以接收新的任务提交,并且还可以正常处理阻塞队列中的任务。
- SHUTDOWN:不再接收新的任务提交,不过线程池可以继续处理阻塞队列中的任务。
- STOP:不再接收新的任务,同时还会丢弃阻塞队列中的既有任务;此外,它还会中断正在处理中的任务。
- TIDYING:所有的任务都执行完毕后(同时也涵盖了阻塞队列中的任务),当前线程池中的活动的线程数量降为0,将会调用terminated方法。
- TERMINATED:线程池的终止状态, 当terminated方法执行完毕后,线程池将会处于该状态之下。
public class ThreadPoolExecutor extends AbstractExecutorService {
//线程池本身的状态,线程池持有线程的数量
//前三位是线程的状态,后面的是线程池的被管理的数量
//1. 线程池本身的状态:ctl的高3位来表示
//2. 线程池中所运行着的线程的数量:ctl的其余29位来表示
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// -3代表减去三种状态
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池本身的状态
//1. RUNNING:线程池可以接收新的任务提交,并且还可以正常处理阻塞队列中的任务。
private static final int RUNNING = -1 << COUNT_BITS;
//2. SHUTDOWN:不再接收新的任务提交,不过线程池可以继续处理阻塞队列中的任务。
private static final int SHUTDOWN = 0 << COUNT_BITS;
//3. STOP:不再接收新的任务,同时还会丢弃阻塞队列中的既有任务;此外,它还会中断正在处理中的任务。
private static final int STOP = 1 << COUNT_BITS;
//4. TIDYING:所有的任务都执行完毕后(同时也涵盖了阻塞队列中的任务),当前线程池中的活动的线程数量降为0,将会调用terminated方法。
private static final int TIDYING = 2 << COUNT_BITS;
//终止
// 5. TERMINATED:线程池的终止状态, 当terminated方法执行完毕后,线程池将会处于该状态之下。
private static final int TERMINATED = 3 << COUNT_BITS;
//当前线程的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//当前线程池中的数量
private static int workerCountOf(int c) { return c & CAPACITY; }
//重新获取ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
//内部类
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
}
核心的execute
- 在线程池中的线程在某个时间执行给定的任务
- 哪个线程执行
- 创新新的线程
- 用线程池的已有的线程
- 阻塞队列满了或者线程池关了 就会导致提交的任务不能执行了
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. 如果运行线程少于corePoolSize,会开辟新线程,用新线程执行任务,addWorker会以原子的方式 去检查运行的状态,运行线程的数量
*
* 2. 如果任务可以成功加到阻塞队列中,会尝试再次检查是否添加了新的线程,
*
* 3. 如何不能加入队列,就会创建新的线程,如果失败,会拒绝任务
*/
int c = ctl.get();
//正在执行的线程 《 核心线程
if (workerCountOf(c) < corePoolSize) {
//创建新线程
if (addWorker(command, true))
return;
c = ctl.get();
}
//插入阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
//可用线程==0 添加线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//线程池关了
else if (!addWorker(command, false))
reject(command);
}
对上面重点方法addWorker的解析(线程添加和创建)
//worker是对线程池的包装
//firstTask是新创建的任务 首先执行任务
//线程数《corePoolSize 或者 阻塞队列满了 直接执行
// true:用corePoolSize判断 false:用maximunPoolSize
//对线程池的线程+1
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//获取线程状态
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//到底应不应该创建线程
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//线程数量
int wc = workerCountOf(c);
//线程线程数量大于线程最大数量 如果》corePoolSize 就用maximumPoolSize比较
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//线程 + 1
if (compareAndIncrementWorkerCount(c))
break retry;
//重新获取最新值
c = ctl.get(); // Re-read ctl
//线程池状态发生变化
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建新的线程 将state = -1,让线程不能被中断
w = new Worker(firstTask);
//获取线程
final Thread t = w.thread;
if (t != null) {
//锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//添加到HashSet中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
//添加到了线程中
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//执行线程
if (workerAdded) {
//会调用run方法
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
线程执行流程解析
//1.addWorker方法里的 start开始
//执行线程
if (workerAdded) {
//会调用run方法
t.start();
workerStarted = true;
}
//2.
public void run() {
runWorker(this);
}
//3.线程池中线程执行任务
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//从worker获取任务
Runnable task = w.firstTask;、
//将任务变成空,防止下次再次执行
w.firstTask = null;
//释放锁 将State 设置为0 释放锁(允许中断)
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try { //getTask()从阻塞队列获取第一个任务
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
//是不是处于STOP
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
//释放排他锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
//处理worker退出
processWorkerExit(w, completedAbruptly);
}
}
线程池关闭
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
//减少线程数
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//记录完成的任务的总数
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
//判断是不是低于 Sotp
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
shutdown
- 关闭线程池,拒绝新任务的提交,不会丢弃阻塞队列的任务
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//权限检查
checkShutdownAccess();
//提前将状态设置为shutdown
advanceRunState(SHUTDOWN);
//中断空闲的workers
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//唤醒阻塞在当前condition上的线程
tryTerminate();
shutdownNow
- 返回阻塞队列的任务的列表
- 正在执行的任务也不执行了
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
//中断所有线程
interruptWorkers();
//将阻塞队列的任务赋值并清除
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
先shutdown 再awaitTermination
public boolean awaitTermination(long timeout, TimeUnit unit) //阻塞的方法
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
//判断线程是否到达TERMINATED
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
//判断是否超时
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
线程名字自定义
Executors.DefaultThreadFactory
然后传参 通过 7个参数的构造方法 实现
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
pool-1(第一个线程池 static修饰 共享)-thread-2(线程池里的第几个线程)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
-
int corePoolSize:线程池当中所一直维护的线程数量,如果线程池处于任务空闲期间,那么该线程也并不会被回收掉
-
int maximumPoolSize:线程池中所维护的线程数的最大数量
-
long keepAliveTime:超过了corePoolSize的线程在经过keepAliveTime时间后如果一直处于空闲状态,那么超过的这部分线程将会被回收掉
-
TimeUnit unit:指的是keepAliveTime的时间单位
-
BlockingQueue<Runnable> workQueue:向线程池所提交的任务位于的阻塞队列,它的实现有多种方式(满了的话就需要 RejectedExecutionHandler(拒绝策略))
-
ThreadFactory threadFactory:线程工厂,用于创建新的线程并被线程池所管理,默认线程工厂所创建的线程都是用户线程且优先级为正常优先级
-
RejectedExecutionHandler handler:表示当线程池中的线程都在忙于执行任务且阻塞队列也已经满了的情况下,新到来的任务该如何被对待和处理。
-
RejectedExecutionHandler 它有四种实现策略:
-
AbortPolicy: 直接抛出一个运行期异常。
-
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } extends RuntimeException
-
DiscardPolicy:默默地丢弃掉提交的任务,什么都不做且不抛出任何异常
-
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { }
-
DiscardOldestPolicy:丢弃掉阻塞队列中存放时间最久的任务(队头元素),并且为当前所提交的任务留出一个队列中的空闲空间,以便将其放进到队列中
-
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { //对头出队 e.getQueue().poll(); e.execute(r); } }
-
CallerRunsPolicy:直接由提交任务的线程来运行这个提交的任务
-
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } }
-
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
public class MyTest2 {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(3, 5, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue(3), new ThreadPoolExecutor.CallerRunsPolicy());
IntStream.range(0,9).forEach(i -> {
//Future<?> submit(Runnable task); runnable返回void
//Callable 有返回值
//<T> Future<T> submit(Runnable task, T result); //返回的结果就是传入的result
executorService.submit(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
IntStream.range(0, 1).forEach(j -> {
System.out.println(Thread.currentThread().getName());
});
});
});
executorService.shutdown();
}
}
对于线程池来说,其提供了execute与submit两种方式来向线程池提交任务
总体来说,submit方法是可以取代execute方法的,因为它既可以接收Callable任务,也可以接收Runnable任务。
关于线程池的总体执行策略:
- 如果线程池中正在执行的线程数 < corePoolSize,那么线程池就会优先选择创建新的线程而非将提交的任务加到阻塞队列中。
- 如果线程池中正在执行的线程数 >= corePoolSize,那么线程池就会优先选择对提交的任务进行阻塞排队而非创建新的线程。
- 如果提交的任务无法加入到阻塞队列当中,那么线程池就会创建新的线程;如果创建的线程数超过了maximumPoolSize,那么拒绝策略就会起作用。
关于线程池任务提交的总结:
- 两种提交方式:submit与execute
- submit有三种方式,无论哪种方式,最终都是将传递进来的任务转换为一个Callable对象进行处理
- 当Callable对象构造完毕后,最终都会调用Executor接口中声明的execute方法进行统一的处理
submit方法解析
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
//RunnableFuture执行任务 返回结果 newTaskFor返回FutureTask:可取消异步的运算
RunnableFuture<Void> ftask = newTaskFor(task, null);
//Executor.execute(Runnable command);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
//rnnable 返回的值是 Null
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result; // result ==null
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
ForkJoinPool
public class ForkJoinPool extends AbstractExecutorService
- fork 把大任务拆成小任务,如果小任务还能拆分 就继续拆分
- join 把任务的结果合并
public ForkJoinPool() {
// 最大数CPU超线程后的核心数
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
defaultForkJoinWorkerThreadFactory, null, false);
}
private ForkJoinPool(int parallelism,
//创建线程
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
int mode,
String workerNamePrefix) {
//线程名 前缀
this.workerNamePrefix = workerNamePrefix;
this.factory = factory;
this.ueh = handler;
this.config = (parallelism & SMASK) | mode;
long np = (long)(-parallelism); // offset ctl counts
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
- RecursiveTask的compute()返回结果
- RecursiceActioncompute() 不返回结果
public class MyTest3 {
public static void main(String[] args) {
//多少个cpu线程
ForkJoinPool forkJoinPool = new ForkJoinPool();
MyTask myTask = new MyTask(1, 100);
int result = forkJoinPool.invoke(myTask);
System.out.println(result);
forkJoinPool.shutdown();
}
}
class MyTask extends RecursiveTask<Integer> {
private int limit = 4;
private int firstIndex;
private int lastIndex;
public MyTask(int firstIndex, int lastIndex) {
this.firstIndex = firstIndex;
this.lastIndex = lastIndex;
}
@Override
protected Integer compute() {
int result = 0;
int gap = lastIndex - firstIndex;
boolean flag = gap <= limit;
if (flag) {
System.out.println(Thread.currentThread().getName());
for (int i = firstIndex; i <= lastIndex; ++i) {
result += i;
}
} else {
int middleIndex = (firstIndex + lastIndex) / 2;
MyTask leftTask = new MyTask(firstIndex, middleIndex);
MyTask rightTask = new MyTask(middleIndex + 1, lastIndex);
//fork
invokeAll(leftTask, rightTask);
int leftTaskResult = leftTask.join();
int rightTaskResult = rightTask.join();
result = leftTaskResult + rightTaskResult;
}
return result;
}
}
CompletionService
- 将新的异步任务生产 和已经结果的异步任务 分开
- 消费者会接受已经完成的任务处理结果,谁先完成处理谁
- 执行顺序和结果顺序不一样
- CompletionService依赖线程池完成任务,仅仅只是管理一个内部的队列
public interface CompletionService<V> {
/*
提交一个有返回值的任务,执行完毕,会获取到
*/
Future<V> submit(Callable<V> task);
/*
返回result
*/
Future<V> submit(Runnable task, V result);
/*
获取 移除下一个任务, 如果没用这个对象 会等待
*/
Future<V> take() throws InterruptedException;
/*
获取完成 移除任务, 如果不存在 返回NULL
*/
Future<V> poll();
/*
获取完成 移除任务, 指定时间内如果不存在 返回NULL
*/
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
public class ExecutorCompletionService<V> implements CompletionService<V>
先执行好 先返回 不是先输入先返回
public class MyTest4 {
public static void main(String[] args) throws Exception {
ExecutorService executorService =
new ThreadPoolExecutor(4, 10, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.AbortPolicy());
CompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
//completionService提交任务
IntStream.range(0, 10).forEach(i -> {
completionService.submit(() -> {
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName());
return i * i;
});
});
for (int i = 0; i < 10; ++i) {
//阻塞队列 take阻塞队列获取对象 get 取具体值
int result = completionService.take().get();
System.out.println(result);
}
executorService.shutdown();
}
}
ThreadLocalRandom
public class ThreadLocalRandom extends Random
- 种子每个线程独有一份 原始的是种子多个线程共享所以需要定义为aotomicLong
static final ThreadLocalRandom instance = new ThreadLocalRandom();
实例共享算法
对于一个随机数生成器来说,有两个要素需要考量:
- 随机数生成器的种子。
- 具体的随机数生成算法(函数)。
对于ThreadLocalRandom来说,其随机数生成器的种子是存放在每个线程的ThreadLocal中的。
public class MyTest4 {
public static void main(String[] args) throws Exception {
ExecutorService executorService = new ThreadPoolExecutor(4, 10, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.AbortPolicy());
CompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
IntStream.range(0, 10).forEach(i -> {
completionService.submit(() -> {
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName());
return i * i;
});
});
for (int i = 0; i < 10; ++i) {
int result = completionService.take().get();
System.out.println(result);
}
executorService.shutdown();
}
}