多線程特性
原子性
所謂原子性即:一個或者多個操作,要麼全部執行并且執行的過程不會被任何因素打斷,要麼就都不執行。
在原子操作中,本質上拒絕多線程操作的,不論是單核或多核伺服器,當要對某一個資料進行原子操作時,同一時刻隻有有一個線程能夠對其進行操作,簡單來說,在整個操作過程中不會被線程排程器打斷,如a=1就是一個原子操作,但a++則不是一個原子操作,因為其内部會額外産生一個新的Integer對象。
舉個例子,假設對一個32位的變量指派,操作分為兩步:低16位指派、高16位指派。當線程A對低16位資料寫入成功後,線程A被中斷。而此時另外的線程B去讀取a的值,那麼讀取到的就是錯誤的資料。
在Java中的原子性操作包括:
基本類型的讀取和指派操作,且指派必須是數字指派給變量,變量之間的互相指派不是原子性操作。
所有引用的指派操作。
java.concurrent.Atomic.* 包中所有原子操作類的一切操作。
測試
public class test {
public static void main(String[] args) throws InterruptedException {
MyInt myInt = new MyInt();
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+"----->"+myInt.getNum());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
static class MyInt{
int num;
public int getNum(){
return num++;
}
}
}
可見性
所謂可見性:即當多個線程通路同一個共享變量時,一個線程修改了該共享變量的值後,其他線程能夠立即檢視到修改後的值。
在多線程環境下,一個線程對共享變量的操作對其他線程是預設是不可見的,也就是說一個線程對某一共享的修改,預設其他線程是無法進行檢視的。而如果要做到可見,Java中的volatile、synchronized、Lock都能保證可見性。如一個變量被volatile修飾後,表示當一個線程修改共享變量後,其會立即被更新到主記憶體中,其他線程讀取共享變量時,會直接從主記憶體中讀取。而synchronized和Lock能保證同一時刻隻有一個線程擷取鎖然後執行同步代碼,并且在釋放鎖之前會将對變量的修改重新整理到主存當中。是以可以保證可見性。
有序性
所謂有序性:即程式執行的順序會按照代碼的先後順序執行。
其可以了解為在本線程内,所有的操作都是有序的。而如果在A線程中觀察B線程,所有的操作都是無序的。在JMM中為了提升程式的執行效率,允許編譯器和處理器對指令重排序。對于單線程來說,指令重排并不會産生問題,而在多線程下則不可以。
在Java中可以通過synchronized和Lock來保證有序性,synchronized和Lock保證每個時刻是有一個線程執行同步代碼,相當于是讓線程順序執行同步代碼,自然就保證了有序性。