启动多线程的两种情况比较
啟動多線程有兩種方式:(都是在主線程main線程下)
1. 使用同一個線程對象來啟多個線程
2. 使用多個線程對象來啟多個線程
?
這兩種方式有什么區(qū)別呢?先貼上代碼舉例說明:
這是使用線程對象MyRunnable的同一個實例r來啟動了兩個線程
MyRunnable r = new MyRunnable(); Thread ta = new Thread(r,"Thread-A"); Thread tb = new Thread(r,"Thread-B"); ta.start(); tb.start();?
這是使用線程對象MyRunnable的兩個不同的實例r來啟動了兩個線程
MyRunnable r1 = new MyRunnable(); MyRunnable r2 = new MyRunnable();Thread ta = new Thread(r1,"Thread-A"); Thread tb = new Thread(r2,"Thread-B"); ta.start(); tb.start();?
那么使用這兩種方式的區(qū)別在哪里呢?我們緊接著看下面的代碼的運行結(jié)果:
public class MyRunnable implements Runnable {private Foo foo =new Foo(); public static void main(String[] args) {MyRunnable r = new MyRunnable();Thread ta = new Thread(r,"Thread-A"); Thread tb = new Thread(r,"Thread-B"); ta.start(); tb.start(); /*MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();Thread ta = new Thread(r1,"Thread-A"); Thread tb = new Thread(r2,"Thread-B"); ta.start(); tb.start();*/} public void run() {for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :當前foo對象的x值= " + foo.getX());} } public int fix(int y) {return foo.fix(y);} }class Foo {private int x = 100;public int getX() {return x;} public int fix(int y) {x = x - y; return x;} }?
①使用同一個線程對象啟多個線程的運行結(jié)果:
Thread-B :當前foo對象的x值= 40
Thread-B :當前foo對象的x值= 10
Thread-A :當前foo對象的x值= -20
Thread-B :當前foo對象的x值= -50
Thread-A :當前foo對象的x值= -50
Thread-A :當前foo對象的x值= -80
?
②使用多個線程對象啟動多個線程的運行結(jié)果:
Thread-A :當前foo對象的x值= 70
Thread-B :當前foo對象的x值= 70
Thread-B :當前foo對象的x值= 40
Thread-A :當前foo對象的x值= 40
Thread-A :當前foo對象的x值= 10
Thread-B :當前foo對象的x值= 10
?
我們可以看到①在改變值的過程中,值串了。并且線程執(zhí)行也是串的,兩個線程之間在相互爭搶執(zhí)行。
(線程ta的run方法還沒有執(zhí)行完,tb的run方法爭搶到了cpu資源從而執(zhí)行)
②在改變值的過程中,值改變是對的。線程執(zhí)行是串的。(值沒有串,是因為foo是私有變量,屬于ta,tb所各自私有)
?
①是我們不能允許的,因為值串了。
①②都出現(xiàn)的線程之間相互爭搶的問題,就看我們的業(yè)務實現(xiàn)了。
使用多線程時,我們有時就是想啟用多個線程同時去干不同的事情,這時它們相互爭搶執(zhí)行就是我們想要的。
有時,在多個線程同時訪問一個方法時,我們希望當一個線程執(zhí)行完這個方法后,再讓其他的線程去執(zhí)行,這時,我們就要避免線程之間相互爭搶的問題,也就是使用同步鎖機制來控制。
?
好,如果我們現(xiàn)在想要run()方法執(zhí)行完了之后,其他線程才能再次進入run()方法來執(zhí)行。我們用同步關鍵字synchronized來實現(xiàn)。如下:
同步方法:
synchronized public void run() {for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :當前foo對象的x值= " + foo.getX());} }同步塊:
public void run() {synchronized(this){for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :當前foo對象的x值= " + foo.getX());} }}?
對于①我們使用上面的同步方法和同步塊都能得到如下的輸出:
Thread-A :當前foo對象的x值= 70
Thread-A :當前foo對象的x值= 40
Thread-A :當前foo對象的x值= 10
Thread-B :當前foo對象的x值= -20
Thread-B :當前foo對象的x值= -50
Thread-B :當前foo對象的x值= -80
?
對于②我們使用上面的同步方法和同步塊卻得到如下的輸出:
Thread-A :當前foo對象的x值= 70
Thread-B :當前foo對象的x值= 70
Thread-A :當前foo對象的x值= 40
Thread-B :當前foo對象的x值= 40
Thread-A :當前foo對象的x值= 10
Thread-B :當前foo對象的x值= 10
?
①的結(jié)果與我們預期的是一樣的,但是②卻不如我們的預期,各個線程之間還是在相互爭搶執(zhí)行。
為什么呢?我們不是都已經(jīng)使用synchronized同步了嗎?
導致這個問題的根源就是對象鎖的問題。
①中使用同步方法時,線程ta,tb對應的對象鎖都為MyRunnable的實例對象r,對象鎖共享且唯一,所以起到了同步的作用。
同理,使用同步塊時,ta,tb的對象鎖也都是MyRunnable的實例對象r,故也能達到效果。
但對于②不同的是,使用方法同步和塊同步時,線程ta,tb對應的對象鎖分別是各自的線程對象的實例,即ta-->r1,tb-->r2。故線程ta,tb分別持有各自的對象鎖,所以達不到同步的效果。
?
如果換成如下代碼執(zhí)行②:
public void run() {synchronized("123"){for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :當前foo對象的x值= " + foo.getX());} }}我們得到如下結(jié)果:
Thread-A :當前foo對象的x值= 70
Thread-A :當前foo對象的x值= 40
Thread-A :當前foo對象的x值= 10
Thread-B :當前foo對象的x值= 70
Thread-B :當前foo對象的x值= 40
Thread-B :當前foo對象的x值= 10
?
這下就和我們的預期一樣了。ta,tb線程都持有字符串"123"作為對象鎖,ta,tb線程中的"123"都指向相同的內(nèi)存地址,故對象鎖相同且共享,故能達到同步效果。(為什么ta,tb中的"123"指向相同的內(nèi)存地址,與String對象本身比較特殊有關,在此不贅述)
對于文章中的對象鎖問題有疑問的,可以參見另一篇博文:http://www.cnblogs.com/kevin-yuan/archive/2013/04/27/3047511.html
轉(zhuǎn)載于:https://www.cnblogs.com/kevin-yuan/p/4111242.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結(jié)
以上是生活随笔為你收集整理的启动多线程的两种情况比较的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: easy ui 使用总结
- 下一篇: 利用matlab将二进制小数转换为十进制