同步 和 併發 : 對於程序員來說,他們其實是有兩個意思的,一個是多線程層面,一個是網站請求服務器層面。
對於網站的請求層面來說:
同步:就是代碼一步一步的有順序的向下執行。
併發:就是多個請求同時訪問一臺服務器。
對於多線程層面來說:
同步: 就是加鎖,爲了保證數據的原子性。也就是保證數據的正確性。
併發:就是多個線程,操作同一個共享的資源。
所以:他們之前是有區別的,不是一個概念。
接下來就說說Lock鎖,他是jdk1.5提供併發包的一個工具。以前沒有Lock鎖的時候,大家都是用synchronized做鎖。那爲什麼用Lock做鎖呢 ?換句話說:就是Lock鎖與synchronized的區別是什麼 ?
用過synchronized的人都知道,synchronized(){} 這後面的{}就是需要被鎖住的代碼。他這個是自動的將包裹的代碼全部上鎖,執行到最後一個花括號就自動解鎖。
而Lock鎖,是手動上鎖,手動解鎖,相對來說更加靈活。而且效率也比synchronized的效率高,jdk1.5專門爲多線程開發工具包,處理了很多效率問題,所以推薦使用。
區別:
1. Lock鎖,可以嘗試非阻塞式的獲取鎖,當線程獲取到鎖之後,就會持有鎖,只要程序沒有手動的釋放鎖,那麼這個鎖是絕對不會被釋放的。還可以在任意位置,手動的釋放鎖。還可以指定時間範圍內獲取到鎖,如果指定時間範圍內,依然沒有獲取到鎖,則返回,重新開始。所以Lock鎖是相當靈活的。
2. synchronized是屬於代碼塊,當代碼執行完畢之後,或者中間出現異常,就會自動的釋放鎖資源,功能比較單一,且效率沒有Lock鎖高。
接下來,就用一下Lock做鎖,並實現一下在使用Lock鎖的情況下,怎麼來完成線程之前的通訊。
public class Person {
public String username;
public int age;
/**
* true 生產者線程等待, false 消費者線程等待
*/
public boolean flag = false;
/**
* 重入鎖
*/
public Lock lock = new ReentrantLock();
}
public class WriteThread extends Thread {
private Person person;
public Condition condition;
public WriteThread(Person person, Condition condition) {
this.person = person;
this.condition = condition;
}
@Override
public void run() {
/**
* 定義一個局部變量。
* 當這個值爲偶數時,爲Person類賦值: 偶數 0
* 當這個數爲奇數時,爲Person類賦值: 奇數 1
*/
int data = 0;
while (true) {
try {
//開啓鎖,注意這個鎖的位置,一定要在上面。
person.lock.lock();
/**
* true: 等待消費
*/
if (person.flag) {
// await 用Lock鎖的線程通訊
condition.await();
}
if (data == 0) {
person.username = "偶數";
person.age = 0;
} else {
person.username = "奇數";
person.age = 1;
}
/**
* 依次改變data的值爲: 偶數、奇數
*/
data = (data + 1) % 2;
person.flag = false;
// signal 用Lock鎖的線程通訊
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**
* 在finally中解鎖。
*/
person.lock.unlock();
}
}
}
}
public class ReadThread extends Thread {
public Person person;
public Condition condition;
public ReadThread(Person person, Condition condition) {
this.person = person;
this.condition = condition;
}
@Override
public void run() {
while (true) {
try {
/**
* 上鎖:
* 使用Lock鎖之後,有個缺點,就是說,如果代碼中間出現異常,如果沒有特殊處理,下面的鎖就一定不會解鎖了。
* 那麼處理這個問題也很簡單,用try catch的finally。
*
* 注意這個鎖的位置,一定要在上面。
*/
person.lock.lock();
if (!person.flag) {
condition.await();
}
System.out.println(person.username + "," + person.age);
person.flag = true;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
/**
* 在finally中解鎖。
*/
person.lock.unlock();
}
}
}
}
public class Test {
public static void main(String[] args) {
Person person = new Person();
/**
* 使用Lock鎖後,線程之前通訊的方式用Condition類。
* Condition底層原理也是 使用wait 和 notify,不過調用的方法改了。
*/
Condition condition = person.lock.newCondition();
WriteThread writeThread = new WriteThread(person, condition);
ReadThread readThread = new ReadThread(person, condition);
writeThread.start();
readThread.start();
}
}