内容學習于:edu.aliyun.com
内容概括:
在多線程的處理之中,可以利用Runnable描述多個線程操作的資源,而Thread描述每一個線程對象,于是當多個線程通路統一資源的時候如果處理不當就會産生資料的錯誤操作。
1. 同步問題的提出
賣票操作代碼:
class MyThread implements Runnable {
private int ticket = 10;//總票數為10
@Override
public void run() {
while (true){
if(this.ticket>0){
System.out.println(Thread.currentThread().getName()+",賣 票、目前票數:"+this.ticket--);
}else {
System.out.println("***票已經賣光***");
break;
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
//三個線程賣5張票
new Thread(mt,"票販子A").start();
new Thread(mt,"票販子B").start();
new Thread(mt,"票販子B").start();
}
}
結果:
票販子B,賣票、目前票數:8
票販子B,賣票、目前票數:9
票販子A,賣票、目前票數:10
票販子B,賣票、目前票數:6
票販子B,賣票、目前票數:7
票販子B,賣票、目前票數:4
票販子A,賣票、目前票數:5
票販子B,賣票、目前票數:2
***票已經賣光***
票販子B,賣票、目前票數:3
***票已經賣光***
票販子A,賣票、目前票數:1
***票已經賣光***
此時的程式将建立三個線程對象,并且這三個線程對象将進行5張票的出售。此時的程式在進行賣票處理的時候并沒有任何的問題(假象),下面可以模拟一下賣票中的延遲操作。
代碼:
class MyThread implements Runnable {
private int ticket = 10;//總票數為10
@Override
public void run() {
while (true) {
if (this.ticket > 0) {
try {
Thread.sleep(100);//模拟網路延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",賣票、目前票數:" + this.ticket--);
} else {
System.out.println("***票已經賣光***");
break;
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
//三個線程賣5張票
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子B").start();
}
}
結果:
票販子B,賣票、目前票數:9
票販子B,賣票、目前票數:10
票販子A,賣票、目前票數:9
票販子B,賣票、目前票數:8
票販子B,賣票、目前票數:8
票販子A,賣票、目前票數:7
票販子B,賣票、目前票數:6
票販子B,賣票、目前票數:6
票販子A,賣票、目前票數:5
票販子A,賣票、目前票數:4
票販子B,賣票、目前票數:3
票販子B,賣票、目前票數:3
票販子B,賣票、目前票數:2
票販子A,賣票、目前票數:2
票販子B,賣票、目前票數:2
票販子B,賣票、目前票數:1
票已經賣光
票販子B,賣票、目前票數:-1
票已經賣光
票販子A,賣票、目前票數:0
票已經賣光
這個時候追加了延遲問題就暴露出來了,而實際上這個問題一直都在。
如下圖所示:
2. 線程同步
經過分析之後已經可以确認同步問題所産生的主要原因了,那麼下面就需要進行同步問題的解決,但是解決同步問題的關鍵是鎖,指的是當某一個線程執行操作的時候,其它線程外面等待;
如果要想在程式之中實作這把鎖的功能,就可以使用synchronized關鍵字來實作,利用此關鍵也可收定義同步方法或同步代碼塊,在同步代碼塊的操作裡面的代碼隻允許一個線程執行。
synchronized(同步對象){
同步代碼操作;
}
一般要進行同步對象處理的時候可以采用目前對象this進行同步。
同步代碼塊解決代碼:
class MyThread implements Runnable {
private int ticket = 10;//總票數為10
@Override
public void run() {
while (true) {
synchronized (this) {//每次隻允許一個線程通路
if (this.ticket > 0) {
try {
Thread.sleep(1);//模拟網路延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",賣票、目前票數:" + this.ticket--);
} else {
System.out.println("***票已經賣光***");
break;
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
//三個線程賣5張票
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子B").start();
}
}
結果:
票販子A,賣票、目前票數:10
票販子A,賣票、目前票數:9
票販子A,賣票、目前票數:8
票販子A,賣票、目前票數:7
票販子A,賣票、目前票數:6
票販子A,賣票、目前票數:5
票販子A,賣票、目前票數:4
票販子A,賣票、目前票數:3
票販子A,賣票、目前票數:2
票販子A,賣票、目前票數:1
***票已經賣光***
***票已經賣光***
***票已經賣光***
(将ticket的數量變多後,就不會一直出現隻有一個票販子賣票的情況了)
加入同步處理之後,程式的整體的性能下降了。同步實際上會造成性能的降低。
同步方法代碼:
class MyThread implements Runnable {
private int ticket = 10;//總票數為10
public synchronized boolean sale(){
if (this.ticket > 0) {
try {
Thread.sleep(100);//模拟網路延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",賣票、目前票數:" + this.ticket--);
return true;
} else {
System.out.println("***票已經賣光***");
return false;
}
}
@Override
public void run() {
while (this.sale()) {}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
//三個線程賣5張票
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子B").start();
}
}
在日後學習Java類庫的時候會發現,系統中許多的類上使用的同步處理采用的都是同步方法。
一定要記住:同步會造成性能下降。
3. 死鎖
死鎖是在進行多線程同步的處理之中有可能産生的一種問題,所謂的死鎖指的是若幹個線程彼此互相等待的狀态。下面通過一個簡單的代碼來觀察一下死鎖的表現形式,但是對于此代碼不作為重點。
代碼:
public class DeadLock implements Runnable {
private Jian jj = new Jian();
private XiaoMing xm = new XiaoMing();
@Override
public void run() {
xm.say(jj);
}
public DeadLock() {
new Thread(this).start();
jj.say(xm);
}
public static void main(String[] args) {
new DeadLock();
}
}
class Jian {
public synchronized void say(XiaoMing xm) {
System.out.println("阿健說:此路是我開,要想從這過,留下買路财10塊");
xm.get();
}
public synchronized void get() {
System.out.println("阿健說:給錢了,我可以讓路了");
}
}
class XiaoMing {
public synchronized void say(Jian jj) {
System.out.println("小明說:先讓我走,我再給錢");
jj.get();
}
public synchronized void get() {
System.out.println("小明說:我走了,錢先賴着");
}
}
結果:
如下圖所示:
現在死鎖造成的主要原因是因為彼此都在互相等待着,等待着對方先讓出資源。死鎖實際上是一種開發中出現的不确定的狀态,有的時候代碼如果處理不當則會不定期出現死鎖,這是屬于正常開發中的調試問題。
若幹個線程通路同一資源時一定要進行同步處理,而過多的同步會造成死鎖。