什麼是多線程
多線程爲了能夠提高應用程序的運行效率,在一個進程中有多條不同的執行路徑,同時並行執行,互不影響。
什麼是線程安全
當多個線程同時共享,同一個全局變量或靜態變量,做寫的操作時,可能會發生數據衝突問題,也就是線程安全問題。但是做讀操作是不會發生數據衝突問題。
解決辦法
使用同步代碼塊或者Lock鎖機制,保證在多個線程共享同一個變量只能有一個線程進行操作
什麼是分佈式鎖
分佈式鎖一般用在分佈式系統或者多個應用中,用來控制同一任務是否執行或者任務的執行順序。在項目中,部署了多個tomcat應用,在執行定時任務時就會遇到同一任務可能執行多次的情況,我們可以藉助分佈式鎖,保證在同一時間只有一個tomcat應用執行了定時任務
Zookeeper實現分佈式鎖原理
使用zookeeper創建臨時序列節點來實現分佈式鎖,適用於順序執行的程序,大體思路就是創建臨時序列節點,找出最小的序列節點,獲取分佈式鎖,程序執行完成之後此序列節點消失,通過watch來監控節點的變化,從剩下的節點的找到最小的序列節點,獲取分佈式鎖,執行相應處理,依次類推……
創建Lock接口
public interface Lock {
//獲取到鎖的資源
public void getLock();
// 釋放鎖
public void unLock();
}
創建ZookeeperAbstractLock抽象類
//將重複代碼寫入子類中..
public abstract class ZookeeperAbstractLock implements Lock {
// zk連接地址
private static final String CONNECTSTRING = "127.0.0.1:2181";
// 創建zk連接
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
protected static final String PATH = "/lock";
public void getLock() {
if (tryLock()) {
System.out.println("##獲取lock鎖的資源####");
} else {
// 等待
waitLock();
// 重新獲取鎖資源
getLock();
}
}
// 獲取鎖資源
abstract boolean tryLock();
// 等待
abstract void waitLock();
public void unLock() {
if (zkClient != null) {
zkClient.close();
System.out.println("釋放鎖資源...");
}
}
}
ZookeeperDistrbuteLock類
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
private CountDownLatch countDownLatch = null;
@Override
boolean tryLock() {
try {
zkClient.createEphemeral(PATH);
return true;
} catch (Exception e) {
// e.printStackTrace();
return false;
}
}
@Override
void waitLock() {
IZkDataListener izkDataListener = new IZkDataListener() {
public void handleDataDeleted(String path) throws Exception {
// 喚醒被等待的線程
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
public void handleDataChange(String path, Object data) throws Exception {
}
};
// 註冊事件
zkClient.subscribeDataChanges(PATH, izkDataListener);
if (zkClient.exists(PATH)) {
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
// 刪除監聽
zkClient.unsubscribeDataChanges(PATH, izkDataListener);
}
}
使用Zookeeper鎖運行效果
public class OrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
// 使用lock鎖
// private java.util.concurrent.locks.Lock lock = new ReentrantLock();
private Lock lock = new ZookeeperDistrbuteLock();
public void run() {
getNumber();
}
public void getNumber() {
try {
lock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",生成訂單ID:" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unLock();
}
}
public static void main(String[] args) {
System.out.println("####生成唯一訂單號###");
// OrderService orderService = new OrderService();
for (int i = 0; i < 100; i++) {
new Thread( new OrderService()).start();
}
}
}
master選舉使用場景及結構
現在很多時候我們的服務需要7*24小時工作,假如一臺機器掛了,我們希望能有其它機器頂替它繼續工作。此類問題現在多采用master-salve模式,也就是常說的主從模式,正常情況下主機提供服務,備機負責監聽主機狀態,當主機異常時,可以自動切換到備機繼續提供服務(這裏有點兒類似於數據庫主庫跟備庫,備機正常情況下只監聽,不工作),這個切換過程中選出下一個主機的過程就是master選舉。
對於以上提到的場景,傳統的解決方式是採用一個備用節點,這個備用節點定期給當前主節點發送ping包,主節點收到ping包後會向備用節點發送應答ack,當備用節點收到應答,就認爲主節點還活着,讓它繼續提供服務,否則就認爲主節點掛掉了,自己將開始行使主節點職責。如圖1所示: