把java基礎擼一邊,從簡單的開始。html
線程部分:java
學習原子類,先來了解一下一個概念,樂觀鎖與悲觀鎖。面試
簡單理解:就是認爲該數據在獲取的時候不會被其餘線程修改,更新的時候判斷一下就能夠了。是否同樣,像是緩存。更新給個標誌就能夠。在前面的介紹的volatile就是個樂觀鎖,自認爲不會被其餘線程修改,其餘線程讀就不會出現髒數據的出現。樂觀鎖,感受就像是個緩存數據同樣,會更新,明知會更新,但取的時候放心大膽樂觀地取。算法
簡單理解,就是和樂觀鎖相反。其餘線程一定會對數據進行修改,不敢放心大膽得取數據,因此要多這個數據要強行鎖住,像是synchronize,對線程進行互斥。保證數據讀取的時候不會被修改。數組
推薦文章:樂觀鎖與悲觀鎖緩存
悲觀鎖這樣對數據進行了保護,操做數據的時候,只有一個線程在場。synchronize就證實了這點,可是這樣讓其餘線程堵塞又執行是很是消耗性能的。那麼樂觀鎖是怎麼保證樂觀鎖的,volatile是確定不行的。bash
volatile,這個修飾詞只能保證可見性。不能保證原子性。併發
public class Demo41 {
public volatile int val = 0 ;
public void set(){
val++;
}
public static void main(String[] age){
Demo41 demo41 = new Demo41();
new Thread(new Runnable() {
@Override
public void run() {
for (;;){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo41.set();
System.out.println(Thread.currentThread().getName() + " val : "+demo41.val);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (;;){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo41.set();
System.out.println(Thread.currentThread().getName() + " val : "+demo41.val);
}
}
}).start();
...
}
}複製代碼
打印結果:ide
Thread-2 val : 12
Thread-0 val : 14
Thread-1 val : 14
Thread-2 val : 15
Thread-0 val : 17
複製代碼
java除了synchronize提供原子性,還提供了不少。好比原子類系列性能
import java.util.concurrent.atomic.Atomic**;複製代碼
public class Demo41 {
public AtomicInteger val = new AtomicInteger(0) ;
public int set(){
return val.getAndIncrement();//自加1
}
public static void main(String[] age){
Demo41 demo41 = new Demo41();
new Thread(new Runnable() {
@Override
public void run() {
for (;;){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (;;){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (;;){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
}
}
}).start();
}
}複製代碼
打印
Thread-0 val : 0
Thread-1 val : 1
Thread-2 val : 2
Thread-0 val : 3
Thread-1 val : 4
Thread-2 val : 5
Thread-0 val : 6複製代碼
AtomicIn系列有不少,下面是AtomicInteger的源碼。我拿個比較簡單的講,標題是簡單看源碼。
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;複製代碼
1:繼承了Number。這個類有一些對數值類型的操做。
2:實現了Serialzable接口。
3:成員變量建立了Unsafe。
4:成員變量中有Long類型valueOffset,private修飾,不能被其餘類改動,能猜到這個字段就是這個類操做的核心了。
4:靜態代碼塊中,對valueOffset作了處理。unsafe.objectFieldOffset(File)這個方法,獲取該屬性在內存中的偏移位置。Long類型用來存儲這個獲取的地址。
5:擁有private和volatile修飾的value int類型值,也就是這個類計算的值了。
在上面實例的代碼中,有兩處使用一個是實例化的時候,一個是自增的時候。
public AtomicInteger val = new AtomicInteger(0) ;
public int set(){
return val.getAndIncrement();
}複製代碼
看構造方法的源碼
public AtomicInteger(int initialValue) {
value = initialValue;
}複製代碼
只是個普通的複製功能。注意,發現5
在來看自加方法
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}複製代碼
這裏使用到了unsafe類的操做。進行查看這個方法的源碼
//內存地址V,舊的預期值A,即將要更新的目標值B。
/*public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = this.getIntVolatile(o, offset); //拿到內存位置的最新值
//拿到內存位置的最新值v,使用CAS嘗試修將內存位置的值修改成目標值v+delta,若是修改失敗,
則獲取該內存位置的新值v,而後繼續嘗試,直至修改爲功
} while(!this.compareAndSwapInt(o, offset, v, v + delta));
return var5;
}*/複製代碼
這裏看到了的是CSA算法,即比較並替換,是一種實現併發算法時經常使用的技術。
這個比較是個do...while循環比較,這裏能夠看到那個Long類型存地址是幹嗎的了,就是爲了取數據的,得到如今內存存取的值,而後拿須要修改的值進行比較,若是是樣的說明修改爲功,若是沒有修改爲功,說明還有人改,因此繼續循環(沒有作重量鎖,如synchronize因此會有多個線程進入)上面的註釋寫了,你們放大點看,也能夠從下面推薦文章中看。
還要注意,爲何能夠及時通知到數據的更改,是擁有有volatile修飾,可見性。在建立原子類的時候,value被volatile修飾。不得不說句,牛逼。
原子類系列,還有能夠操做對象,數組。大同小異,就不一一介紹了。謝謝各位觀看。
推薦文章:面試畢問的CAS,你懂嗎?