把java基礎擼一邊,從簡單的開始。java
線程部分:緩存
存在都是有理由的。若是沒有線程咱們的程序只能是下圖這個樣子,想象一個下假若有1千萬個請求,每一個請求1秒,這得請求多長時間
bash
若是不止一個窗口處理事情,這個時候,線程的優勢就體現出來了,這樣執行完這麼多請求就除4了多線程
有點是會體現出來,但同時也暴露出了多線程的不足。一個CPU只能運行一個線程。四核也就是能夠運行4個線程,平時看這開100個線程也沒事,一下就執行完了,可是這些CPU運行的時間片斷過短,執行快因此看上去也沒事,可是若是超過了必定範疇也會出問題,(這不是本章的重點),還有就是數據容易成爲髒數據,若是多個線程去修改同一個int,那麼這個字符串最終是什麼樣呢?還有各類搶佔資源,死鎖...等等。jvm
本章重點說的是髒數據問題。其餘的後續更新ide
實例,多線程下髒數據的出現:spa
public class Demo21 extends Thread{
private int va = 1;
@Override
public void run() {
for (int i = 0 ; i < 100 ; i++){
va ++ ;
System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
}
}
public static void main(String[] age){
Demo21 demo21 = new Demo21();
demo21.start();
Demo21 demo22 = new Demo21();
demo22.start();
Demo21 demo33 = new Demo21();
demo33.start();
}
}複製代碼
三個線程開啓,對va進行自加1。按照理想狀態,是1 2 3 ...300操作系統
可是結果是什麼樣呢?.net
Thread Name :Thread-0va :2
Thread Name :Thread-2va :2
Thread Name :Thread-1va :2
Thread Name :Thread-2va :3
Thread Name :Thread-0va :3
複製代碼
三個線程 同時打印2。明明va++很短,執行很快爲何仍是會出現這個狀況。線程
簡單看一下jvm的運行空間是什麼回事,這裏看兩個區域,線程共享區,線程獨佔區。常量是會放到線程共享區的,也就是說沒個線程均可以拿到這個值,而線程獨佔區,裏面的數據只能夠被當前線程享用(線程之間的數據通訊另說)。這樣就能夠了解到爲何會出現這個狀況。
1:數據不是能夠立刻寫的,從地中中讀到數據進入CPU緩存,CPU再作修改,修改完以後在給到主存中
2:A線程修改數據B線程並不知道。
線程一個危險就是這個髒數據,破壞了數據的一致性 。解決這些方法java中提供了不少操做
synchronize,AtomicIntege,LongAdder
此次主要介紹synchronize
public class Demo21 implements Runnable{
private int va = 1;
public void get() {
synchronized (Demo21.class){
va++;
System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
}
}
@Override
public void run() {
for (int i = 0 ; i < 100 ; i++){
get();
}
}
public static void main(String[] age){
Demo21 demo21 = new Demo21();
Thread thread = new Thread(demo21);
Thread thread1 = new Thread(demo21);
Thread thread2 = new Thread(demo21);
thread.start();
thread1.start();
thread2.start();
}
}複製代碼
打印結果
Thread Name :Thread-0va :2
Thread Name :Thread-0va :3
Thread Name :Thread-0va :4
Thread Name :Thread-0va :5
Thread Name :Thread-1va :6
Thread Name :Thread-0va :7
Thread Name :Thread-0va :8
Thread Name :Thread-0va :9
Thread Name :Thread-0va :10
複製代碼
能夠見到加了這個關鍵字就能夠順序執行。
參考一下java synchronize原理總結,對synchronize作一個初步瞭解
synchronize的底層是使用操做系統的mutex lock實現
內存可見性:一個線程對共享變量值的修改,可以及時被其餘線程看到。
操做原子性:持有同一個鎖的兩個同步快只能串行進入
synchronize保證 1,2,3,4線程執行synchronize修飾包括的程序代碼塊中,是保證進入的只有一個線程。synchronize能夠理解爲一個只容許一個線程進入的大門,進來一個就用它持有的鎖給鎖住,防止其餘線程進入,當代碼塊執行完畢以後,再開啓。
java是面向對象的語言,synchronize用的鎖也是存在java對象頭裏。
synchronized有三種使用方式:
1:修飾實例方法
2:修飾靜態方法
3:修飾代碼塊
修飾實例方法:
public class A {
public void A(){
System.out.println("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void B(){
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}複製代碼
public class Demo21 {
public static void main(String[] age){
A a = new A();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.A();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.B();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
}
}複製代碼
若是沒有synchronize修飾
A
B
1001
1001
複製代碼
是沒有串行執行的
public synchronized void A()
public synchronized void B()複製代碼
修飾實例方法以後
A
1001
B
2000
複製代碼
能夠看到是串行執行,有鎖的效果
public class Demo21 {
public static void main(String[] age){
A a = new A();
A a1 = new A();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.A();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a1.B();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
}
}複製代碼
若是是不一樣的實例執行A B方法
A
B
1001
1001
複製代碼
這樣就能夠知道,修飾非靜態實例方法的鎖,就是它的實例對象
修飾靜態方法:
public static synchronized void B()
public static synchronized void A()
複製代碼
修飾靜態方法以後
A
B
1000
2000
複製代碼
建立兩個實例,也是串行執行,這樣能夠證實是,當前類加鎖,進入同步代碼塊錢要獲取當前類對象的鎖
修飾代碼塊
public void A(){
synchronized (A.class){
System.out.println("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void B(){
synchronized (A.class) {
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}複製代碼
代碼執行後
A
B
1001
2000
複製代碼
在synchronize(對象.class)這個對象就是這個同步方法的鎖了
這裏就是對象synchronize作了一下簡單的認識,其實還有不少複雜的東西,要了解synchronize還須要知道jvm,操做系統等等。但能力不足,就介紹到這裏。
推薦三篇文章: