<div class="iteye-blog-content-contain" style="font-size: 14px"></div>
這個總結将對線程中的資源共享和線程同步問題進行簡單的分析。
線程:是程序中的一個單一的連續控制流程。
一個程序可以含有多個線程,那下面就談談多線程。
java中多線程的實作有兩種手段:1 繼承Thread類 2實作Runnable接口
這裡以買火車票為例。
class Ticket extends Thread {
public int tickets = 10;
public String name;
public Ticket(String name){
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println(this.name+"買票"+this.tickets--);
}
}
}
public class Test1 {
public static void main(String []arg){
Ticket t1 = new Ticket("線程1");
Ticket t2 = new Ticket("線程2");
Ticket t3 = new Ticket("線程3");
t1.run();
t2.run();
t3.run();
}
}
看到運作結果了吧,它們都是順序執行的,這說明我們的調用方法不對,當我們把run方法改為start方法後才出現我們想要的結果.那我們來看看run方法和start方法的差別。
run()方法:在本線程内調用該Runnable對象的run()方法,可以重複多次調用;
start()方法:啟動一個線程,調用該Runnable對象的run()方法,不能多次啟動一個線程;
run()方法隻是一個普通的方法,如果直接調用run方法的話那程式中還是隻有一個主線程,并沒有達到多線程的目的,主程式中還是會順序執行,執行完一個run方法後才會繼續執行下一個方法。而start()方法是負責啟動一個線程,不用等待執行完其中的run方法後再執行下面的方法,而是直接執行下面的方法。簡單的說就是你在run方法中寫入要執行的代碼,然後你隻要調用start方法就可以了,至于什麼時候執行run方法,那就不是你負責的了。start()方法的功能其實是向cpu申請另一個線程空間來執行run()方法的,它和目前的線程空間是相對獨立的。就是說如果你直接調用run方法的話它仍然會執行,但是在目前的線程空間,是以它會按順序執行,而調用start方法後,run方法就會和目前的代碼并行的執行,進而達到多線程。
另一個我們要考慮的問題就是資源共享問題。這個問題也可以用來差別Thread和Runnable,而多線程在很多情況下都是處理資源共享的問題。下面我們仍以買火車票為例來看看資源共享的問題。
class Ticket extends Thread {
public int tickets = 10;
public String name;
public void run() {
while (this.tickets > 0) {
System.out.println(this.name + " 賣票:" + this.tickets--);
}
}
public Ticket(String name) {
this.name = name;
}
}
public class Test1 {
public static void main(String[] arg) {
Ticket t1 = new Ticket("線程1");
Ticket t2 = new Ticket("線程2");
Ticket t3 = new Ticket("線程3");
t1.start();
t2.start();
t3.start();
}
}
對于這個的運作結果我們之前已經試過了,從結果上看,它雖然實作了多線程,但它好像并不符合實際,我們定義一共有10張票,分三個地方來買,那就是說三個地方共享這10張票,可結果出現了30個數,顯然是不對的,那如果把代碼改一下:
class Ticket implements Runnable {
public int tickets = 10;
public String name;
public void run() {
while (this.tickets > 0) {
System.out.println("賣票:" + this.tickets--);
}
}
}
public class Test1 {
public static void main(String args[]) {
// 準備四個售票點
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
Thread t3 = new Thread(d);
t1.start();
t2.start();
t3.start();
}
}
再運作,結果:
賣票:10
賣票:9
賣票:8
賣票:7
賣票:6
賣票:5
賣票:4
賣票:3
賣票:2
賣票:1
資源共享的問題也算是解決了。
還有一個問題就是線程同步的問題,這也是值得我們注意的問題,把上面的代碼再進行修改:
class Demo implements Runnable {
private int ticket = 10;
public void run() {
while (this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("賣票:" + this.ticket--);
}
}
};
public class Test1 {
public static void main(String args[]) {
// 準備四個售票點
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
Thread t3 = new Thread(d);
t1.start();
t2.start();
t3.start();
}
};
運作結果:
賣票:10
賣票:9
賣票:8
賣票:7
賣票:6
賣票:5
賣票:4
賣票:3
賣票:2
賣票:1
賣票:0
賣票:-1
出現了兩個不該出現的結果,那我們來分析一下出現這個結果的原因。
我們分三個售票點共同買10張票,當票數隻剩一張的時候,假如被A拿走了,然後進入sleep中,而在這個時候,B也将最後一張票拿走了,而此時的tickets還等于1,當A把tickets放回的時候,tickets--變為了0,當B再放回的時候則出現了-1的情況。這就是不用步造成的。
那我們就要解決這個不同步的問題
下面介紹一下synchronized關鍵字:
當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。
當兩個并行的線程同時通路object的一個synchronized修飾的同步塊是,同一時間隻能有一個線程可以對其通路,隻有當他通路完之後,另一個才能對其通路。
建立一個同步方法:
class Demo implements Runnable {
private int ticket = 10;
public synchronized void method(){
while (this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 賣票:" + this.ticket--);
}
}
public void run() {
method();
}
};
public class Test1 {
public static void main(String args[]) {
// 準備四個售票點
Demo d = new Demo();
Thread t1 = new Thread(d,"a");
Thread t2 = new Thread(d,"b");
Thread t3 = new Thread(d,"c");
t1.start();
t2.start();
t3.start();
}
};
運作結果:
b 賣票:10
b 賣票:9
b 賣票:8
b 賣票:7
b 賣票:6
b 賣票:5
b 賣票:4
b 賣票:3
b 賣票:2
b 賣票:1
這個結果雖然對票數同步了,但似乎又出現了其他的問題,一個售票點就把所有的票賣完了,回頭看看我們的代碼是哪裡出了問題。
做如下改正:
class Demo implements Runnable {
private int ticket = 10;
public synchronized void method() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 賣票:"
+ this.ticket--);
}
public void run() {
while (this.ticket > 0) {
method();
}
}
};
運作結果:
a 賣票:10
a 賣票:9
a 賣票:8
a 賣票:7
a 賣票:6
a 賣票:5
c 賣票:4
c 賣票:3
b 賣票:2
b 賣票:1
c 賣票:0
a 賣票:-1
又出問題了(無奈),看看又是什麼情況,我們在同步方法中加一個if(this.tickets>0)保護,在運作,看看結果吧... ...終于沒問題了。