JAVA學習筆記之多線程專題(一):線程同步安全處理

關於多線程操作,我相信大家都不陌生,如何開啓一個線程之類我想就不用太詳細的去描述,今天我們就來講講線程同步的安全的問題。

對於線程同步安全問題,一般是一個多線程對同一個資源同時操作的時候,會出現資源同時操作造成線程不安全的問題。那麼這個時候我們需要去對公共資源進行同步保護。這個時候有三種情況

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文檔。

以上就是線程同步安全的內容。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章