進程與線程
進程:系統資源調度與分配的基本單位,一個進程至少有一個線程,進程裏面的線程共享進程資源(網絡、磁盤空間、內存)
線程:cpu分配的基本單位(CPU資源共享)
線程共享區和線程獨享區
線程的生命週期
新建狀態(new):當線程對象創建後,即新建狀態;
就緒狀態(Runnable):當線程調用start()方法時,線程進入就緒狀態,等待CPU時間片的分配;
運行狀態(Running):當就緒狀態的線程獲取到CPU分配的時間片時,此刻線程進入運行狀態;
阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,
直到其進入到就緒狀態,纔有機會再次被CPU調用以進入到運行狀態;
阻塞的分類
① 同步阻塞:當前線程執行時候需要去獲取鎖的時候(鎖可能被其他線程佔用未被釋放),此刻當前線程會
進入阻塞狀態 (synchronized);
② 等待阻塞:當前線程調用wait()方法時,會進入等待阻塞狀態,直到當前線程被通知纔會繼續執行,一般和
notify()和notifyAll()方法聯合使用;
③ 其他阻塞:當前線程調用join()會sleep()會進入阻塞狀態,當sleep()狀態超時、join()等待線程終止或者超時、
或者I/O處理完畢時,線程重新轉入就緒狀態;
死亡狀態(Dead):當前線程執行完成或者異常退出,那麼當前線程生命週期結束。
線程創建的幾種方式
方式1:通過繼承Thread創建線程,重寫run()方法實現業務
public class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("嗨!我是通過繼承Thread類創建的線程"+this.getName()+",麼麼噠!!!");
}
public static void main(String[] args) {
//方式1運行
ThreadDemo threadDemo = new ThreadDemo();
Thread thread = new Thread(threadDemo);
thread.start();
//方式2運行
new ThreadDemo().start();
}
}
嗨!我是通過繼承Thread類創建的線程Thread-2,麼麼噠!!!
嗨!我是通過繼承Thread類創建的線程Thread-0,麼麼噠!!!
方式2:實現Runnable接口創建線程類,重寫run()實現業務
public class ThreadDemo1 implements Runnable {
private static volatile int money = 1;
/**
* @see Thread#run()
*/
@Override
public void run() {
System.out.println("嗨,我是通過實現Runnable接口創建的線程"+Thread.currentThread().getName()+"!!!麼麼噠");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new ThreadDemo1());
thread.start();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread1 修改成員屬性---> " + ++money);
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread2 修改成員屬性---> " + ++money);
}
});
thread2.start();
}
}
嗨,我是通過實現Runnable接口創建的線程Thread-0!!!麼麼噠
thread1 修改成員屬性---> 2
thread2 修改成員屬性---> 3
方式3:通過實現Callable接口創建線程,重寫call()方法實現業務
@Data
@ToString
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public class ThreadDemo2 implements Callable<User> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public User call() throws Exception {
System.out.println("嗨!我是實現Callable<T> 接口創建的線程"+Thread.currentThread().getName()+",我有返回值哦,麼麼噠!!!");
return new User("麼麼噠", 1);
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadDemo2 threadDemo2 = new ThreadDemo2();
FutureTask<User> futureTask = new FutureTask<User>(threadDemo2);
new Thread(futureTask).start();
User user = futureTask.get();
System.out.println(user);
}
}
嗨!我是實現Callable<T> 接口創建的線程Thread-0,我有返回值哦,麼麼噠!!!
User(name=麼麼噠, age=1)
方式4:使用線程池例如用Executor框架,後續再詳解
幾種方式創建線程優缺點比較
通過繼承Thread:
優點:編寫簡單,如果需要訪問當前線程,則無須使用Thread.currentThread()方法,直接使用this即可獲得當前線程;
缺點:繼承了Thread不能再繼承其他類,擴展性不好,不適應於多線程;
通過實現Runnable接口:
優點:可以繼承其他類、擴展性好;通過上面thread1、thread2操作money可以看出,實現接口可以多線程target的共享,適用於多線程;
缺點:編程稍稍複雜,不能使用Thread的方法,如果需要訪問當前線程,則必須使用Thread.currentThread()方法;
通過實現Callable接口:
優點:可以繼承其他類、擴展性好;多個線程可以共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況,非常適合創建多線程;和Runnable不同的是,該方式有返回值,可以做一些業務處理;
缺點:編程稍稍複雜,不能使用Thread的方法,如果需要訪問當前線程,則必須使用Thread.currentThread()方法;
總結:Thread編程簡單,不適用多線程環境;Runnable、Callable編程稍微複雜點,但是非常適合多線程環境;
Callable有返回值;
線程常用方法介紹
Thread.currentThead():獲取當前線程對象
getPriority():獲取當前線程的優先級
setPriority():設置當前線程的優先級
注意:線程優先級高,被CPU調度的概率大,不代表一定會運行,還有小概率運行優先級低的線程.
isAlive():判斷線程是否處於活動狀態 (線程調用start後,即處於活動狀態)
join():調用join方法的線程強制執行,其他線程處於阻塞狀態,等該線程執行完後,其他線程再執行;有可能被外界
中斷產生 InterruptedException 中斷異常。
public final void join() throws InterruptedException;
public final synchronized void join(long millis) throws InterruptedException;
public final synchronized void join(long millis, int nanos) throws InterruptedException;
public class ThreadJoinDemo {
public static void main(String[] args) throws InterruptedException {
final Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + Thread.currentThread().getState());
}
});
Thread t2 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + Thread.currentThread().getState());
}
});
t1.start();
t2.start();
System.out.println(Thread.currentThread() + "wait " + t1.getName() + " " + t2.getName() + " over!");
t1.join();
t2.join();
System.out.println(Thread.currentThread() + " final " + Thread.currentThread().getState());
//情況1: main wait ---> main final ---> t1 ---> t2 (假設join不會進行cpu調度)
//情況2: main wait ---> t1 ---> t2 ---> main final (假設Join會進行cpu調度)
//結果
//Thread[main,5,main]wait Thread-0 Thread-1 over!
//Thread-1 : RUNNABLE
//Thread-0 : RUNNABLE
//Thread[main,5,main] final RUNNABLE
//結論:join()會進行cpu調度,執行當前線程,其他線程進入阻塞狀態,等待當前線程執行完成!
//可能出現以下結果,由於cpu時間片分批(cpu控制的),可以去人的是:main] final一定在t1/t2之後.
//Thread-1 : RUNNABLE
//Thread[main,5,main]wait Thread-0 Thread-1 over!
//Thread-0 : RUNNABLE
//Thread[main,5,main] final RUNNABLE
}
}
sleep():Thread類的靜態方法,讓出執行權暫時不參與CPU調度,但是不會釋放鎖,當前調用線程進入阻塞狀態;當時間到了,重新進入就緒狀態,重新等待cpu調度.
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
public class ThreadSleepDemo {
public static void main(String[] args) throws InterruptedException {
//創建一把鎖
final Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
//加鎖
synchronized (lock) {
System.out.println("t1 :" + Thread.currentThread().getName() + " : " + Thread.currentThread().getState());
//在哪個線程裏面調用,就代表當前線程,此處代表t1線程,(t1線程先休眠1秒讓出時間片)
Thread.sleep(1000);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//加鎖
synchronized (lock) {
System.out.println("t2 :" + Thread.currentThread().getName() + " : " + Thread.currentThread().getState());
}
}
});
t1.start();
t2.start();
System.out.println("main thread " + Thread.currentThread().getName() + " " + Thread.currentThread().getState());
//情況1: main ---> t1 sleep() ---> t2 ---->t1 (假設sleep會釋放鎖)
//情況2: main ---> t1 sleep() ---> t2 lock so t1 ---> t2 (假設sleep不會釋放鎖)
//main thread main RUNNABLE
//t1 :Thread-0 : RUNNABLE
//t2 :Thread-1 : RUNNABLE
//結論:sleep()沒有釋放鎖,注意main 和t1的順序可能是下面這樣的,這個要看cpu的調度,唯一可以確定的是t1一定在t2前面;
//t1 :Thread-0 : RUNNABLE
//main thread main RUNNABLE
//t2 :Thread-1 : RUNNABLE
}
}
yield():Thread的靜態方法,讓出剩餘時間片但不會釋放鎖,當前線程進入就緒狀態.(cpu可能還是會調用本線程)
public static native void yield();
public class ThreadYieldDemo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " ---> yield start ");
Thread.yield();
System.out.println(Thread.currentThread().getName() + " ---> yield over ");
}
});
t.start();
}
//分析:
//情況1:yield start ---> yield over ---> yield start ... (yield()方法不會讓出剩餘時間片)
//情況2:yield start ---> yield start ---> yield over (會出現連續...) (yield()方法讓出剩餘時間片)
//結果:Thread-0 ---> yield start
//Thread-2 ---> yield start
//Thread-4 ---> yield start
//Thread-4 ---> yield over
//Thread-3 ---> yield start
//Thread-1 ---> yield start
//Thread-7 ---> yield start
//Thread-1 ---> yield over
//Thread-8 ---> yield start
//Thread-8 ---> yield over
//Thread-3 ---> yield over
//Thread-6 ---> yield start
//Thread-6 ---> yield over
//Thread-2 ---> yield over
//Thread-5 ---> yield start
//Thread-0 ---> yield over
//Thread-5 ---> yield over
//Thread-9 ---> yield start
//Thread-7 ---> yield over
//Thread-9 ---> yield over
//注:結果不是一定.
//結論:1.yield會讓出時間片 2.調用yield的線程處於就緒狀態,可能還是會被cpu調用 3.不釋放鎖驗證機制同上.
}
}
interrupt()、interrupted()、isInterrupted():Interrupt()中斷線程,打上中斷標識,線程仍會繼續執行;
interrupted():檢驗線程是否中斷,並且清除中斷標識;
isInterrupted():檢測線程是否中斷,不清除中斷標識;
注:調用一個打上中斷線程標識的線程會拋出異常
public void interrupt();
public static boolean interrupted();
public boolean isInterrupted();
public class ThreadInterruptDemo {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName() + " interrupted " + Thread.currentThread().getState());
}
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " isInterrupted " + Thread.currentThread().getState());
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " isInterrupted " + Thread.currentThread().getState());
}
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName() + " interrupted " + Thread.currentThread().getState());
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
mainThread.interrupt();
System.out.println(Thread.currentThread().getName() +" ---> run over !");
}
});
t1.start();
t2.start();
t1.interrupt();
t2.interrupt();
t3.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + Thread.currentThread().getState());
//分析t1:
// 情況1:interrupted()不清除中斷標識,結果則應該爲interrupted ---> isInterrupted
// 結果1:main RUNNABLE
//Thread-0 interrupted RUNNABLE
//結論1:interrupted()清除中斷標識,且可以斷定interrupt()並沒有中斷線程,只是打上標識.
//分析t2:
//情況2:如果isInterrupted()清除中斷標識,結果應該爲結果則應該爲 isInterrupted
//結果2:main RUNNABLE
//Thread-1 isInterrupted RUNNABLE
//Thread-1 interrupted RUNNABLE
//結論2:如果isInterrupted不會清除中斷標識
//分析t3:
//結論:調用一個被中斷的線程(例子中的main線程)會拋出異常.
}
}
Thread-2 ---> run over !
main RUNNABLE
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at gitee.activity.thread.ThreadInterruptDemo.main(ThreadInterruptDemo.java:56)
wait()、notify()、notifyAll():該方法要在同步方法或者同步代碼塊中才使用的.
wait():調用線程等待,進入阻塞狀態,直到notify()、notifyAll()才能從新進入就緒狀態,等待cpu調度繼續執行;
public final void wait() throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
public class ThreadWaitDemo {
static Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " run over ");
lock.wait(2000);
}
}
});
final Thread t2 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " run over ");
//lock.notify();
}
}
});
t1.start();
t2.start();
Thread.sleep(1000);
if("TERMINATED".equals(t1.getState().toString()) && "TERMINATED".equals(t2.getState().toString())){
System.out.println("未超時:"+Thread.currentThread().getName() + " wait " + t1.getName() + " wait " + t2.getName());
}
Thread.sleep(3000);
if("TERMINATED".equals(t1.getState().toString()) && "TERMINATED".equals(t2.getState().toString())){
System.out.println("超時:"+Thread.currentThread().getName() + " wait " + t1.getName() + " wait " + t2.getName());
}
//分析:
//情況1:wait()不釋放鎖,結果應該爲: Thread-0 run over --->
//情況2:wait()方法鎖,結果應該爲: Thread-0 run over ---> Thread-1 run over
//結果:Thread-0 run over ...
//Thread-1 run over ...
//超時:main wait Thread-0 wait Thread-1
//結論1:wait()方法鎖 ;
//結論2:wait(long timeout) 超時自動喚醒.
//結論3:notify()可以主動喚醒wait()線程.(//lock.notify();)
}
}
sleep()與wait()區別與共同點
區別1:sleep()是Thread的方法,而wait()和notify()、notifyAll()是Object方法;
區別2:sleep()不會釋放鎖,wait()會釋放鎖;
共同點1:都是使調用線程進入阻塞狀態;
共同點2:都支持指定等待時間wait(long timeout)、join(long timeout).
併發編程優缺點
優點1:充分利用多核CPU的計算能力,性能得到提升,效率提高;
優點2:方便進行業務拆分,提升應用性能.
缺點1:當併發量沒有達到’臨界點‘時,併發編程頻繁的上下文切換反而降低性能;
缺點2:併發編程最大的問題,線程安全問題,共享變量的可見性問題是很大的挑戰.
CPU保證原子性操作(硬件層面)
經典案例i++
比如同時有2個線程執行這段代碼,假如初始時i的值爲0,那麼我們希望兩個線程執行完之後i的值變爲2,然而在多線程中-->
初始時,兩個線程分別讀取i的值存入各自所在的CPU的高速緩存當中,然後線程1進行加1操作,然後把i的最新值1寫入到內存。此時線程2的高速緩存當中i的值還是0,進行加1操作之後,i的值爲1,然後線程2把i的值寫入內存。
最終結果i的值是1,而不是2。這就是著名的緩存一致性問題
爲了解決緩存不一致性問題,通常來說有以下2種解決方法:
1)通過在總線加LOCK#鎖的方式
2)通過緩存一致性協議
在早期的CPU當中,是通過在總線上加LOCK#鎖的形式來解決緩存不一致的問題。因爲CPU和其他部件進行通信都是通過總線來進行的,如果對總線加LOCK#鎖的話,也就是說阻塞了其他CPU對其他部件訪問(如內存),從而使得只能有一個CPU能使用這個變量的內存。比如上面例子中 如果一個線程在執行 i = i +1,如果在執行這段代碼的過程中,在總線上發出了LCOK#鎖的信號,那麼只有等待這段代碼完全執行完畢之後,其他CPU才能從變量i所在的內存讀取變量,然後進行相應的操作。這樣就解決了緩存不一致的問題。
但是上面的方式會有一個問題,由於在鎖住總線期間,其他CPU無法訪問內存,導致效率低下。
所以就出現了緩存一致性協議。最出名的就是Intel 的MESI協議,MESI協議保證了每個緩存中使用的共享變量的副本是一致的。它核心的思想是:當CPU寫數據時,如果發現操作的變量是共享變量,即在其他CPU中也存在該變量的副本,會發出信號通知其他CPU將該變量的緩存行置爲無效狀態,因此當其他CPU需要讀取這個變量時,發現自己緩存中緩存該變量的緩存行是無效的,那麼它就會從內存重新讀取。
CAS原子性操作
Compare And Set(或Compare And Swap),CAS是解決多線程並行情況下使用鎖造成性能損耗的一種機制,CAS操作包含三個操作數——內存位置(V)、預期原值(A)、新值(B)。如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值,否則一直循環(自旋)直到成功。
在java中可以通過鎖和循環CAS的方式來實現原子操作。Java中 java.util.concurrent.atomic包相關類就是 CAS的實現
代碼展示:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger ai = new AtomicInteger();
private int i = 0;
public static void main(String[] args) {
final Counter cas = new Counter();
List<Thread> ts = new ArrayList<Thread>();
// 添加100個線程
for (int j = 0; j < 100; j++) {
ts.add(new Thread(new Runnable() {
public void run() {
// 執行100次計算,預期結果應該是10000
for (int i = 0; i < 100; i++) {
cas.count();
cas.safeCount();
}
}
}));
}
//開始執行
for (Thread t : ts) {
t.start();
}
// 等待所有線程執行完成
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("非線程安全計數結果:"+cas.i);
System.out.println("線程安全計數結果:"+cas.ai.get());
}
/** 使用CAS實現線程安全計數器 */
private void safeCount() {
for (;;) {
int i = ai.get();
// 如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值
boolean suc = ai.compareAndSet(i, ++i);
if (suc) {
break;
}
}
}
/** 非線程安全計數器 */
private void count() {
i++;
}
}
//結果:
非線程安全計數結果:9671
線程安全計數結果:10000
CAS採用自旋高效解決了原子性問題,存在三個缺點:
- 循環時間時間長的話會造成cpu大的開銷:CAS自旋長時間不成功的話,會給CPU帶來非常大的執行開銷。
- ABA問題:因爲CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A。 從Java1.5開始JDK的 atomic包裏提供了一個類AtomicStampedReference 來解決ABA問題。這個類的 compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標誌是否等於預期標誌,如果全部相等,則以原子方式將該引用和該標誌的值設置爲給定的更新值。
public class CasABADemo {
private static AtomicStampedReference<Integer> asr = new AtomicStampedReference<Integer>(1, 1);
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(1000);
boolean flag = asr.compareAndSet(asr.getReference(), 2, asr.getStamp(), asr.getStamp() + 1);
System.out.println(" 1 ---> 2 -->" + flag + " 結果:" + asr.getReference());
flag = asr.compareAndSet(asr.getReference(), 1, asr.getStamp(), asr.getStamp() + 1);
System.out.println(" 2 ---> 1 -->" + flag + " 結果:" + asr.getReference());
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
int beforeStamp = asr.getStamp();
System.out.println("修改前版本號:" + beforeStamp);
Thread.sleep(2000);
int afterStamp = asr.getStamp();
System.out.println("修改後版本號:" + afterStamp);
//此刻已經放手ABA,1-->2-->1
boolean flag = asr.compareAndSet(asr.getReference(), 5, beforeStamp, asr.getStamp() + 1);
System.out.println("版本號匹配不成功:" + flag + " 當前版本:" + asr.getStamp() + " 傳入的版本" + beforeStamp);
flag = asr.compareAndSet(asr.getReference(), 5, afterStamp, asr.getStamp() + 1);
System.out.println("版本號匹配成功:" + flag + " 當前版本:" + asr.getStamp() + " 傳入的版本" + beforeStamp + " 最終理想值:" + asr.getReference());
}
});
t2.start();
}
//結果:
// 修改前版本號:1
// 1 ---> 2 -->true 結果:2
// 2 ---> 1 -->true 結果:1
//修改後版本號:3
//版本號匹配不成功:false 當前版本:3 傳入的版本1
//版本號匹配成功:true 當前版本:4 傳入的版本1 最終理想值:5
//結論:通過添加版本好-->解決了ABA問題.
}
- 只能保證一個共享變量的原子性:當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。
public class CasDemo {
public static AtomicInteger ai = new AtomicInteger(0);
public static void add() throws InterruptedException {
ai.addAndGet(1);
Thread.sleep(1000);
ai.addAndGet(9);
System.out.println("計算和之後:" + ai.get());
}
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
es.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
add();
}
});
}
es.shutdown();
}
//分析:如果add()的輸出結果都爲10的整數倍,那麼說明AtomicInteger保證方法原子性;反之不報賬.
//結果:
// 計算和之後:32
//計算和之後:32
//計算和之後:32
//計算和之後:50
//計算和之後:41
//結論:CAS保證單個共享變量的原子性操作,但是不保證多個共享變量原子性和成員方法的原子性.
}
JAVA對象組成
- 對象頭
- 實例數據
- 對齊填充字節
對象頭的組成
- Mark Word
- 指向類的指針
- 數組長度(只有數組對象纔有)
Mark Word
Mark Word記錄了對象和鎖有關的信息,當這個對象被synchronized關鍵字當成同步鎖時,圍繞這個鎖的一系列操作都和Mark Word有關。
Mark Word在32位JVM中的長度是32bit,在64位JVM中長度是64bit。
Mark Word在不同的鎖狀態下存儲的內容不同,在32位JVM中:
指向類的指針
該指針在32位JVM中的長度是32bit,在64位JVM中長度是64bit,java類信息(類長什麼樣子,有些什麼屬性和行爲)存放在元空間;
數組長度
只有數組對象纔有該值,該數據在32位和64位JVM中長度都是32bit。
實例數據
接下來實例數據部分是對象真正存儲的有效信息,也既是我們在程序代碼裏面所定義的各種類型的字段內容,無論是從父類繼承下來的,還是在子類中定義的都需要記錄下來。 這部分的存儲順序會受到虛擬機分配策略參數(FieldsAllocationStyle)和字段在Java源碼中定義順序的影響。HotSpot虛擬機 默認的分配策略爲longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),從分配策略中可以看出,相同寬度的字段總是被分配到一起。在滿足這個前提條件的情況下,在父類中定義的變量會出現在子類之前。如果 CompactFields參數值爲true(默認爲true),那子類之中較窄的變量也可能會插入到父類變量的空隙之中。
對齊填充
第三部分對齊填充並不是必然存在的,也沒有特別的含義,它僅僅起着佔位符的作用。由於HotSpot VM的自動內存管理系統要求對象起始地址必須是8字節的整數倍,換句話說就是對象的大小必須是8字節的整數倍。對象頭正好是8字節的倍數(1倍或者2倍),因此當對象實例數據部分沒有對齊的話,就需要通過對齊填充來補全。