启动多线程的两种情况比较
啟動(dòng)多線程有兩種方式:(都是在主線程main線程下)
1. 使用同一個(gè)線程對(duì)象來(lái)啟多個(gè)線程
2. 使用多個(gè)線程對(duì)象來(lái)啟多個(gè)線程
?
這兩種方式有什么區(qū)別呢?先貼上代碼舉例說(shuō)明:
這是使用線程對(duì)象MyRunnable的同一個(gè)實(shí)例r來(lái)啟動(dòng)了兩個(gè)線程
MyRunnable r = new MyRunnable(); Thread ta = new Thread(r,"Thread-A"); Thread tb = new Thread(r,"Thread-B"); ta.start(); tb.start();?
這是使用線程對(duì)象MyRunnable的兩個(gè)不同的實(shí)例r來(lái)啟動(dòng)了兩個(gè)線程
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ū)別在哪里呢?我們緊接著看下面的代碼的運(yùn)行結(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() + " :當(dāng)前foo對(duì)象的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;} }?
①使用同一個(gè)線程對(duì)象啟多個(gè)線程的運(yùn)行結(jié)果:
Thread-B :當(dāng)前foo對(duì)象的x值= 40
Thread-B :當(dāng)前foo對(duì)象的x值= 10
Thread-A :當(dāng)前foo對(duì)象的x值= -20
Thread-B :當(dāng)前foo對(duì)象的x值= -50
Thread-A :當(dāng)前foo對(duì)象的x值= -50
Thread-A :當(dāng)前foo對(duì)象的x值= -80
?
②使用多個(gè)線程對(duì)象啟動(dòng)多個(gè)線程的運(yùn)行結(jié)果:
Thread-A :當(dāng)前foo對(duì)象的x值= 70
Thread-B :當(dāng)前foo對(duì)象的x值= 70
Thread-B :當(dāng)前foo對(duì)象的x值= 40
Thread-A :當(dāng)前foo對(duì)象的x值= 40
Thread-A :當(dāng)前foo對(duì)象的x值= 10
Thread-B :當(dāng)前foo對(duì)象的x值= 10
?
我們可以看到①在改變值的過(guò)程中,值串了。并且線程執(zhí)行也是串的,兩個(gè)線程之間在相互爭(zhēng)搶執(zhí)行。
(線程ta的run方法還沒(méi)有執(zhí)行完,tb的run方法爭(zhēng)搶到了cpu資源從而執(zhí)行)
②在改變值的過(guò)程中,值改變是對(duì)的。線程執(zhí)行是串的。(值沒(méi)有串,是因?yàn)閒oo是私有變量,屬于ta,tb所各自私有)
?
①是我們不能允許的,因?yàn)橹荡恕?br />①②都出現(xiàn)的線程之間相互爭(zhēng)搶的問(wèn)題,就看我們的業(yè)務(wù)實(shí)現(xiàn)了。
使用多線程時(shí),我們有時(shí)就是想啟用多個(gè)線程同時(shí)去干不同的事情,這時(shí)它們相互爭(zhēng)搶執(zhí)行就是我們想要的。
有時(shí),在多個(gè)線程同時(shí)訪問(wèn)一個(gè)方法時(shí),我們希望當(dāng)一個(gè)線程執(zhí)行完這個(gè)方法后,再讓其他的線程去執(zhí)行,這時(shí),我們就要避免線程之間相互爭(zhēng)搶的問(wèn)題,也就是使用同步鎖機(jī)制來(lái)控制。
?
好,如果我們現(xiàn)在想要run()方法執(zhí)行完了之后,其他線程才能再次進(jìn)入run()方法來(lái)執(zhí)行。我們用同步關(guān)鍵字synchronized來(lái)實(shí)現(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() + " :當(dāng)前foo對(duì)象的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() + " :當(dāng)前foo對(duì)象的x值= " + foo.getX());} }}?
對(duì)于①我們使用上面的同步方法和同步塊都能得到如下的輸出:
Thread-A :當(dāng)前foo對(duì)象的x值= 70
Thread-A :當(dāng)前foo對(duì)象的x值= 40
Thread-A :當(dāng)前foo對(duì)象的x值= 10
Thread-B :當(dāng)前foo對(duì)象的x值= -20
Thread-B :當(dāng)前foo對(duì)象的x值= -50
Thread-B :當(dāng)前foo對(duì)象的x值= -80
?
對(duì)于②我們使用上面的同步方法和同步塊卻得到如下的輸出:
Thread-A :當(dāng)前foo對(duì)象的x值= 70
Thread-B :當(dāng)前foo對(duì)象的x值= 70
Thread-A :當(dāng)前foo對(duì)象的x值= 40
Thread-B :當(dāng)前foo對(duì)象的x值= 40
Thread-A :當(dāng)前foo對(duì)象的x值= 10
Thread-B :當(dāng)前foo對(duì)象的x值= 10
?
①的結(jié)果與我們預(yù)期的是一樣的,但是②卻不如我們的預(yù)期,各個(gè)線程之間還是在相互爭(zhēng)搶執(zhí)行。
為什么呢?我們不是都已經(jīng)使用synchronized同步了嗎?
導(dǎo)致這個(gè)問(wèn)題的根源就是對(duì)象鎖的問(wèn)題。
①中使用同步方法時(shí),線程ta,tb對(duì)應(yīng)的對(duì)象鎖都為MyRunnable的實(shí)例對(duì)象r,對(duì)象鎖共享且唯一,所以起到了同步的作用。
同理,使用同步塊時(shí),ta,tb的對(duì)象鎖也都是MyRunnable的實(shí)例對(duì)象r,故也能達(dá)到效果。
但對(duì)于②不同的是,使用方法同步和塊同步時(shí),線程ta,tb對(duì)應(yīng)的對(duì)象鎖分別是各自的線程對(duì)象的實(shí)例,即ta-->r1,tb-->r2。故線程ta,tb分別持有各自的對(duì)象鎖,所以達(dá)不到同步的效果。
?
如果換成如下代碼執(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() + " :當(dāng)前foo對(duì)象的x值= " + foo.getX());} }}我們得到如下結(jié)果:
Thread-A :當(dāng)前foo對(duì)象的x值= 70
Thread-A :當(dāng)前foo對(duì)象的x值= 40
Thread-A :當(dāng)前foo對(duì)象的x值= 10
Thread-B :當(dāng)前foo對(duì)象的x值= 70
Thread-B :當(dāng)前foo對(duì)象的x值= 40
Thread-B :當(dāng)前foo對(duì)象的x值= 10
?
這下就和我們的預(yù)期一樣了。ta,tb線程都持有字符串"123"作為對(duì)象鎖,ta,tb線程中的"123"都指向相同的內(nèi)存地址,故對(duì)象鎖相同且共享,故能達(dá)到同步效果。(為什么ta,tb中的"123"指向相同的內(nèi)存地址,與String對(duì)象本身比較特殊有關(guān),在此不贅述)
對(duì)于文章中的對(duì)象鎖問(wèn)題有疑問(wèn)的,可以參見(jiàn)另一篇博文:http://www.cnblogs.com/kevin-yuan/archive/2013/04/27/3047511.html
轉(zhuǎn)載于:https://www.cnblogs.com/kevin-yuan/p/4111242.html
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的启动多线程的两种情况比较的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: easy ui 使用总结
- 下一篇: CodeFirst EF中导航属性的个人