首页 > 代码库 > JAVA学习笔记之多线程专题(一):线程同步安全处理

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

以上就是线程同步安全的内容。




JAVA学习笔记之多线程专题(一):线程同步安全处理