文章出處 https://www.jianshu.com/p/b913e64db7ee
文章對應視頻出處:https://developer.aliyun.com/course/1012?spm=5176.10731542.0.0.6ef2d290hxQ4g0
在多線程的處理之中,可以利用Runnable描述多個線程操作的資源,而Thread描述每一個線程對象,當然多個線程訪問同一資源時如果處理不當,就會產生數據的錯誤操作。
同步問題的引出
現在編寫一個簡單的賣票程序,將若干個線程對象實現賣票的處理操作。
範例:實現賣票操作
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() + "賣票,ticket = " + this.ticket--);
} else {
System.out.println("***** 票賣光了 *****");
break;
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt=new MyThread();
new Thread(mt,"票販子A").start();
new Thread(mt,"票販子B").start();
new Thread(mt,"票販子C").start();
}
}
此時的程序將創建3個線程對象,並且這三個線程將進行10張票的出售。此時的程序在進行賣票處理的時候,並沒有任何的問題(假象),下面可以模擬一下賣票中的延遲操作。
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() + "賣票,ticket = " + this.ticket--);
} else {
System.out.println("***** 票賣光了 *****");
break;
}
}
}
}
這個時候追加了延遲問題就暴露出來了,而實際上這個問題一直都在。
線程同步
經過分析之後已經可以確定同步問題產生的主要原因了,那麼下面就需要進行同步問題的解決,但是解決問題的關鍵是鎖,指的是當某一個線程執行操作的時候,其他線程外面等待;
如果想程序中實現鎖功能,就可以使用synchronized關鍵字來是吸納,利用synchronized可以定義同步方法和同步代碼塊,在同步代碼塊的操作中的代碼只允許一個線程執行。
1、利用同步代碼塊進行處理:
synchronized (同步對象){
同步代碼操作;
}
一般要進行同步對象處理時,可以採用當前對象this進行同步。
範例:利用同步代碼塊解決數據同步訪問問題
class MyThread implements Runnable {
private int ticket = 1000;//總票數爲10張
@Override
public void run() {
while (true) {
synchronized (this) {//每一次只允許一個線程進行訪問
if (this.ticket > 0) {
try {
Thread.sleep(100);//模擬網絡延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
} else {
System.out.println("***** 票賣光了 *****");
break;
}
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子C").start();
}
}
加入同步處理之後,程序的整體性能下降了。同步實際上會造成性能的降低。
2、利用同步方法解決:只需要在方法定義上使用synchronized關鍵字即可。
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() + "賣票,ticket = " + this.ticket--);
return true;
} else {
System.out.println("***** 票賣光了 *****");
return false;
}
}
@Override
public void run() {
while (this.sale()) {}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子C").start();
}
}
在日後學習Java類庫時,系統中許多的類上使用的同步處理採用的都是同步方法。
死鎖
死鎖是在進行多線程同步的處理之中有可能產生的一種問題,所謂的死鎖指的是若干個線程彼此互相等待的狀態。
public class DeadLock implements Runnable {
private Producer producer = new Producer();
private Customer customer = new Customer();
public DeadLock() {
new Thread(this).start();
customer.say(producer);
}
public static void main(String[] args) {
new DeadLock();
}
@Override
public void run() {
producer.say(customer);
}
}
class Producer {
public synchronized void say(Customer customer) {
System.out.println("店員:先買單後吃飯");
customer.get();
}
public synchronized void get() {
System.out.println("收到錢,可以給你做飯了");
}
}
class Customer {
public synchronized void say(Producer producer) {
System.out.println("顧客:先吃飯後買單");
producer.get();
}
public synchronized void get() {
System.out.println("吃飽飯了,可以買單了");
}
}
現在死鎖造成的主要原因是因爲彼此都在互相等待着,等待着對方先讓出資源。死鎖實際上是一種開發中出現的不確定的狀態,有時代碼處理不當,則會不定期出現死鎖,這屬於正常開發中的調試問題。
若干個線程訪問同一資源時一定要進行同步處理,但過多的同步會造成死鎖。