1. 程式人生 > >Java多執行緒之多個執行緒訪問共享物件和資料的方式

Java多執行緒之多個執行緒訪問共享物件和資料的方式

1.如果每個執行緒執行的程式碼相同,可以使用同一個Runable物件,這個Runable物件中有那個共享資料,例如賣票系統就可以這樣做。

package javaplay.test;

public class MulteThreadShareData {
	public static void main(String[] args) {
		ShareData shareData = new ShareData();
		new Thread(shareData).start();
		new Thread(shareData).start();
	}

	static class ShareData implements Runnable {
		int count = 100;

		@Override
		public void run() {
			while (count > 0) {
				decrease();
			}
		}

		public synchronized void decrease() {
			count--;
			System.out.println(Thread.currentThread().getName() + "this count: " + count);
		}

	}
}

這個示例是錯誤的,對共享變數的訪問必須必須加鎖!volatile只能保證可見性,不能保證原子性!
2.如果每個執行緒執行的程式碼不相同,這個時候需要用到不同的Runable物件,有如下兩種方式實現多個Runable物件中的資料共享。

2.1 將共享資料封裝在另外一個物件中,然後將這個物件逐一傳遞給各個Runnable物件。每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,這樣容易實現針對該資料進行的各個操作的互斥和通訊。 

package javaplay.test;

public class MulteThreadShareData {
	public static void main(String[] args) {
		final ShareData shareData = new ShareData();
		new Thread(new Decrease(shareData)).start();
		new Thread(new Increment(shareData)).start();
	}

	static class Decrease implements Runnable {
		private ShareData shareData;

		public Decrease(ShareData shareData) {
			this.shareData = shareData;
		}

		@Override
		public void run() {
			shareData.decrease();
		}

	}

	static class Increment implements Runnable {
		private ShareData shareData;

		public Increment(ShareData shareData) {
			this.shareData = shareData;
		}

		@Override
		public void run() {
			shareData.increment();
		}

	}

	static class ShareData {
		int count = 100;

		public synchronized void decrease() {
			count--;
			System.out.println(Thread.currentThread().getName() + "decrease this count: " + count);
		}

		public synchronized void increment() {
			count++;
			System.out.println(Thread.currentThread().getName() + "increment this count: " + count);
		}
	}
}

2.2 將這些Runnable物件作為某一個類中的內部類,共享資料作為這個外部類中的成員變數,每個執行緒對共享資料的操作方法也分配給外部類,以便實現對共享資料進行的各個操作的互斥和通訊,作為內部類的各個Runnable物件呼叫外部類的這些方法。

package javaplay.test;

public class MulteThreadShareData {
	static int count = 100;

	public static void main(String[] args) {
		new Thread(new Decrease()).start();
		new Thread(new Increment()).start();

	}

	public synchronized static void decrease() {
		count--;
		System.out.println(Thread.currentThread().getName() + "decrease this count: " + count);
	}

	public synchronized static void increment() {
		count++;
		System.out.println(Thread.currentThread().getName() + "increment this count: " + count);
	}

	static class Decrease implements Runnable {
		@Override
		public void run() {
			decrease();
		}

	}

	static class Increment implements Runnable {
		@Override
		public void run() {
			increment();
		}

	}
}

2.3 上面兩種方式的組合:將共享資料封裝在另外一個物件中,每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,物件作為這個外部類中的成員變數或方法中的區域性變數,每個執行緒的Runnable物件作為外部類中的成員內部類或區域性內部類。 

package javaplay.test;

public class MulteThreadShareData {
	public static void main(String[] args) {
		final ShareData shareData = new ShareData();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					shareData.decrease();
				}
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					shareData.increment();
				}

			}
		}).start();
	}

	static class ShareData {
		int count = 100;

		public synchronized void decrease() {
			count--;
			System.out.println(Thread.currentThread().getName() + "this count: " + count);
		}

		public synchronized void increment() {
			count++;
			System.out.println(Thread.currentThread().getName() + "this count: " + count);
		}
	}
}

2.4 總之,要同步互斥的幾段程式碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊。