1.对象头(主要是一些运行时的数据) 2.实例数据 3.对齐填充
长度 | 内容 | 说明 |
---|---|---|
32/64bit | Mark Word | 用于存储自身运行时数据,如hashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等。 |
32/64bit | Class Meta Address | 指向对象类型数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 |
32/64bit | Array Length | 数组的长度(当对象为数据时) |
MarkWord这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,
32bit空间的25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0.
32位虚拟机在不同状态下markword的结构如下图。
大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得的代价更低而引入偏向锁。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
如果有两个线程来竞争锁的话,此时锁就会膨胀,偏向锁就被升级为轻量级锁,这也是常说的锁膨胀。
偏向锁的获取过程:
锁撤销的过程:
由于锁撤销花销挺大,所以,如果某些同步代码块大多数情况下都是有两个或者两个以上的线程来竞争的话,那么偏向锁就是一种累赘,我们可以一开始就把偏向锁关掉。
轻量级锁的加锁过程:
轻量级锁的释放:
由轻量级锁切换到重量锁,是发生在轻量锁释放锁的期间,之前在获取锁的时候它拷贝了锁对象头的Markword,在释放锁的时候如果它发现它持有锁的期间有其他线程来
尝试获取锁了,并且该线程对Markword做了修改,两者对比发现不一致,则切换到重量锁。
轻量级锁有两种:
1.自旋锁 2.自适应自旋锁
自旋锁:指当另一个线程来竞争时,这个线程会在原地等待,而不是把该线程给阻塞,直到那个获得锁的线程释放锁之后,这个线程就可以马上获得锁。
但是,锁在原地循环的时候,是会消耗CPU的,相当于一个什么也不做的for循环。所以,轻量级锁适用于那些同步代码块执行的很快的场景,这样,线程在原地等待较短的时间就可以获取锁了。
由于线程在原地空转是消耗CPU的,我们可以给空循环设置一个次数,当线程超过了这个次数,我们就认为,继续自旋不合适了,此时锁会继续膨胀,升级为重量级锁。
默认情况下,自旋的次数为10次,用户可以通过-XX:PreBlockSpin来进行更改
自适应自旋锁:
自适应自旋锁就是线程自旋次数不是固定的,而是根据实际情况来改变自旋等待的时间。
大概原理:
假如线程1刚刚获得了锁,释放之后,线程2获得了锁,但是线程2在运行的过程中,线程1又想获取锁,但是线程2还没有释放,所以线程1只能自旋等待,但是虚拟机认为由于线程1
刚刚获得过锁,所以线程1这次获得锁的几率还是很大的,所以会延长线程1的自旋次数。
重量级锁是依赖对象内部的monitor对象来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被称为互斥锁。
为什么说重量级锁开销大呢?
当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗CPU,
但是阻塞或者唤醒一个线程时,都需要操作系统帮忙,这就需要从用户态转换到内核态,
而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
参考网址:https://www.cnblogs.com/lzh-blogs/p/7477157.html
// start 方法源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
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();
会抛出IllegalThreadStateException异常,由于start0()方法的实现已经封装在dll中了,但是肯定是在start0方法中修改了线程状态值threadStatus,如果再次进入,就会抛异常。
如果我们每次使用一个线程就去创建一个线程,使用起来非常方便,但是频繁创建线程会大大降低系统效率,因为创建和销毁线程都需要时间。在java中,我们可以通过线程池来实现线程的复用。ThreadPoolExecutor是线程池中最核心的一个类。
// 构造方法1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
// 构造方法2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
// 构造方法3
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
// 构造方法4
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;
}
ThreadPoolExecutor一共有4个构造方法,但是从代码可以看出来,前三个构造方法都是调用最后一个构造方法进行的初始化工作。
corePoolSize:核心池的大小。
在创建了线程池之后,默认情况下,线程池中并没有任何线程,而是等待有任务来了才去创建线程。除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,这两个方法是预先创建线程的,即在没有任务来临之前,就先创建了corePoolSize个线程或者一个线程。但是默认情况下,线程池的线程数为0,当有任务到来时,才创建线程执行任务,当线程池中的数量达到了corePoolSize的时候,就把接下来的线程放入缓存队列。
maximumPoolSize:线程池最大线程数。它表示线程池中最多能创建多少线程。
keepAliveTime:表示线程没有任务执行时最多保持多长时间会终止。默认情况下,只有线程池的线程数量大于corePoolSize的时候,keepAliveTime才会起作用,如果一个线程的空闲时间达到了keepAliveTime,则会终止,直到线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,就算线程池中的线程数不大于corePoolSize,keepAliveTime参数也会起作用。
unit:keepAliveTime的时间单位。有7种取值。
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:一个阻塞队列,用来存储等待执行的任务。一般来说,这个参数有以下几种选择。
threadFactory:线程工厂。主要用来创建线程
handler:表示拒绝处理任务时的策略,当运行线程数已经到达maximumPoolSize时,队列也已经装满时,会调用该参数值拒绝任务。默认情况是AbortPolicy。
有以下四种取值:
AbortPolicy:直接抛出异常。
CallerRunsPolicy:只用调用者所在线程来执行任务。
DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。
public class ThreadPoolExecutor extends AbstractExecutorService { … }
public abstract class AbstractExecutorService implements ExecutorService { … }
public interface ExecutorService extends Executor { … }
public interface Executor { … }
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中ctl是ThreadPoolExecutor的状态变量。
workerCountOf():方法取得当前线程池的线程数量,算法是将ctl的值取低29位。
runStateOf():方法取得线程池的状态,算法是将ctl的值取高3位。
RUNNING 111:表示正在运行
SHUTDOWN 000:表示拒绝接收新的任务。
STOP 001:表示拒绝接收新的任务,并且不再处理任务队列中剩余的任务,还要中断正在执行的任务
TIDYING 010:表示所有线程已经停止,准备执行terminated()方法。
TERMINATED 011:表示已经执行完terminated()方法。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 第三种情况
else if (!addWorker(command, false))
reject(command);
}
以上三种情况分别对应以下:
1.线程池的线程数量小于corePoolSize核心线程任务数量,开启核心线程执行任务。
2.线程池的线程数量不小于corePoolSize核心线程数量,或者开启核心线程失败,尝试将线程以非阻塞的方式添加到任务队列。
3.任务队列已满导致添加任务失败,开启新的非核心线程任务。
一个固定线程数量的线程池。可控制线程最大并发数,超出的线程会在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
// corePoolSize和maximumPoolSize大小一样,同时传入一个无界阻塞队列,
// 该线程池的线程数会维持在指定线程数,不会进行回收(根据worker逻辑分析)
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量。
public static ExecutorService newCachedThreadPool() {
// corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE
// 就是说来一个任务就创建一个worker,回收时间是60s
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可缓存线程池:
1.线程数无限制
2.有空闲线程则复用空闲线程,没有就创建新线程
3.一定程度减少频繁创建/销毁线程,减少系统开销
可以理解为线程数为1的FixedThreadPool。
public static ExecutorService newSingleThreadExecutor() {
// corePoolSize和maximumPoolSize大小都为1,
// 线程池中只有一个线程在运行,其他的都放入阻塞队列。
// 外面包装的FinalizableDelegatedExecutorService类实现了finalize方法,
// 在JVM垃圾回收的时候会关闭线程池。
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
单线程化的线程池:
1.有且只有一个工作线程执行任务
2.所有任务按照指定顺序执行,遵循队列的先入先出
支持定时以指定周期循环执行任务。前三种线程池是ThreadPoolExecutor不同配置的实例,最后一种是ScheduledThreadPoolExecutor的实例。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
转载自:https://blog.csdn.net/u010983881/article/details/79322499