關於多線程操作,我相信大家都不陌生,如何開啓一個線程之類我想就不用太詳細的去描述,今天我們就來講講線程同步的安全的問題。
對於線程同步安全問題,一般是一個多線程對同一個資源同時操作的時候,會出現資源同時操作造成線程不安全的問題。那麼這個時候我們需要去對公共資源進行同步保護。這個時候有三種情況
1、同步代碼塊,這個同步的鎖是任意一個對象;
2、方法同步,這個同步的鎖就是該方法所在的類;
3、靜態方法同步,這個同步的鎖是該方法所在類的字節碼。
接下來,我們舉一個例子來說明多線程對同一個資源進行操作的時候,如何達到資源同步安全的問題。我們以生產消費的例子,生產一個商品,消費一個商品的循環。同時講解線程同步中線程等待和喚醒的用法。
請看代碼:
public class ThreadTest {
public static void main(String[] args) {
// 資源
Resources r = new Resources();
// 生產工廠
Producer pro = new Producer(r);
// 消費者
Consumer con = new Consumer(r);
// 四個線程同時進行生產和消費,實現生產一個消費一個,避免出現對資源的重複混亂應用
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
public static class Resources {
public Resources() {
};
// 產品名稱
private String name;
// 產品序號
private int count = 1;
// 循環標記位
private boolean flag = false;
// 生產方法
public synchronized void produce(String name) {
// 循環標記位
while (flag) {
try {
this.wait();
} catch (Exception e) {
}
}
// 生產出一個產品
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "製造一個商品"
+ this.name);
// 標記位轉換,進入循環
flag = true;
// 爲避免喚醒的線程隊列中的第一個線程不是消費線程,我們選擇全部喚醒
this.notifyAll();
}
// 消費方法
public synchronized void consume() {
while (!flag) {
try {
this.wait();
} catch (Exception e) {
}
}
System.out.println(Thread.currentThread().getName() + "消耗一個商品"
+ this.name);
// 標記位轉換,進入循環
flag = false;
// 爲避免喚醒的線程隊列中的第一個線程不是生產線程,我們選擇全部喚醒
this.notifyAll();
}
}
// 生產者
static class Producer implements Runnable {
private Resources res;
Producer(Resources res) {
this.res = res;
}
public void run() {
while (true) {
res.produce("商品");
}
}
}
// 消費者
static class Consumer implements Runnable {
private Resources res;
Consumer(Resources res) {
this.res = res;
}
public void run() {
while (true) {
res.consume();
}
}
}
}
我們發現,在上述過程中,我們會喚醒所有的等待線程,然後在根據循環標示爲去控制生產與消費。這樣總給是非常不好的感覺。當線程多的時候,會造成線程浪費。在JDK5.0之後,提供了線程同步鎖Lock類來解決這一個問題。使用Lock類可以替代同步關鍵字來達到對公共資源的保護。我看直接看代碼:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest2 {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
static class Resource {
// 新建一個鎖
private Lock lock = new ReentrantLock();
private String name;
private int count = 1;
private boolean flag = false;
// 拿到對應的喚醒條件
private Condition proCondition = lock.newCondition();
private Condition conCondition = lock.newCondition();
public void product(String name) {
// 上鎖
lock.lock();
try {
while (flag) {
// 等待
proCondition.await();
}
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "生產者"
+ this.name);
flag = true;
// 喚醒消費線程
conCondition.signal();
} catch (Exception e) {
} finally {
// 解鎖
lock.unlock();
}
}
public void consume() {
// 上鎖
lock.lock();
try {
while (!flag) {
conCondition.wait();
}
System.out.println(Thread.currentThread().getName() + "消費者----"
+ this.name);
flag = false;
// 喚醒生產者
proCondition.signal();
} catch (Exception e) {
} finally {
// 解鎖
lock.unlock();
}
}
}
static class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.product("商品");
}
}
}
static class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.consume();
}
}
}
}
關於Lock類的更多使用方法大家可以參照JDK文檔。
以上就是線程同步安全的內容。