Java新技术---线程学习之常用同步工具类

 

常用同步工具类学习

Lock&Condition实现线程同步通信  

     Lock比传统模式的synchronized方式更加面向对象,与生活中的锁类似,锁本事也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它必须用同一个Lock对象。锁是上在代表要操作的资源类的内部方法中,而不是线程代码中。

【例子】

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
	public static void main(String[] args) {
		final Outputer outputer = new Outputer();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("德艺双馨");
				}
			}
		}).start();// 线程1

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("index80");
				}
			}
		}).start();// 线程2
	}
}

// 创建一个共享类,加锁
class Outputer {
	Lock lock = new ReentrantLock();

	public void output(String name) {
		lock.lock();// 加锁
		try {
			for (int i = 0; i < name.length(); i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();
		} finally {
			lock.unlock();// 解锁
		}
	}
}

       读写锁:分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,写锁和写锁互斥。如果你写的代码只读数据,可以很多人同时读,但是不能同时写,那就上读锁;如果修改数据,只能一个人在写,且不能同时读,那就上写锁。总之,读的时候上读锁,写的时候上写锁。

【读写锁模拟缓存】

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
 * 模拟计算机缓存
 */

public class CacheDemo {

	private Map<String, Object> cache = new HashMap<String, Object>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

	private ReadWriteLock rwl = new ReentrantReadWriteLock();

	public Object getData(String key) {
		rwl.readLock().lock();// 上读锁
		Object value = null;
		try {
			value = cache.get(key);
			if (value == null) {
				rwl.readLock().unlock();// 如果没有数据解读锁
				rwl.writeLock().lock();// 并加上写锁
				try {
					if (value == null) {
						value = "去数据库拿数据queryDB();";// 实际是去queryDB();
					}
				} finally {
					rwl.writeLock().unlock();
				}
				rwl.readLock().lock();
			}
		} finally {
			rwl.readLock().unlock();
		}
		return value;
	}
}

       Condition的功能类似于传统线程技术中的Object wait和Object notify的功能,在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition总是应该在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。 一个锁内部可以有多个Condition,即有多路等待通知。

【例子】

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * Condition实现多线程通信
 */

public class ThreeConditionCommunication {

	public static void main(String[] args) {

		final Business business = new Business();
		new Thread(new Runnable() {

			@Override
			public void run() {

				for (int i = 1; i <= 50; i++) {
					business.sub2(i);
				}
			}
		}).start();// 线程2

		new Thread(new Runnable() {

			@Override
			public void run() {

				for (int i = 1; i <= 50; i++) {
					business.sub3(i);
				}
			}
		}).start();// 线程3

		for (int i = 1; i <= 50; i++) {
			business.main(i);// 主线程
		}
	}

	static class Business {
		Lock lock = new ReentrantLock();
		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		private int shouldSub = 1;

		public void sub2(int i) {
			lock.lock();
			try {
				while (shouldSub != 2) {
					try {
						condition2.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub2 thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 3;
				condition3.signal();
			} finally {
				lock.unlock();
			}
		}

		public void sub3(int i) {
			lock.lock();
			try {
				while (shouldSub != 3) {
					try {
						condition3.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 20; j++) {
					System.out.println("sub3 thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 1;
				condition1.signal();
			} finally {
				lock.unlock();
			}
		}

		public void main(int i) {
			lock.lock();
			try {
				while (shouldSub != 1) {
					try {
						condition1.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 100; j++) {
					System.out.println("main thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 2;
				condition2.signal();
			} finally {
				lock.unlock();
			}
		}

	}
}

       Semaphere可以维护访问当前自身的的线程个数,并且提供了同步机制。使用Semaphere可以控制同时访问的资源的线程个数,例如,实现一个文件允许的并发访问数。

      Semaphere实现的功能类似于厕所只有5个坑,假如有10个人要上厕所,那么只能有同时5个能够占用,当这5个人中任何1个离开后,其中在等待的另外5个人中又有1个可以占用。等待的5个人可以按随机优先级,也可以按先来后到顺序,这取决与构造Semaphere时传人的参数选项。

      单个信号Semaphere对象可以实现互斥的功能,与传统锁不同的是,Semaphere可以由一个线程获得锁而另外的线程解锁,这可用于死锁恢复场合。

【例子】

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/*
 * 一个厕所5个坑,同时可以有5个人上,模拟10个人上厕所
 */

public class SemaphoreTest {
	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();// 线程池
		final Semaphore sp = new Semaphore(5);// 同时可以有5个线程抢占,模拟一个厕所5个坑
		for (int i = 0; i < 10; i++) {// 创建10个线程,模拟10个人上厕所
			Runnable runnable = new Runnable() {
				public void run() {
					try {
						sp.acquire();//没信号量可用时,将进行阻塞等
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					System.out
							.println(Thread.currentThread().getName()
									+ "进入,当前可用茅坑" + sp.availablePermits()
									+ "个");
					System.out.println(Thread.currentThread().getName()
							+ "即将离开");
					sp.release();
					// 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
					System.out.println(Thread.currentThread().getName()
							+ "已离开,当前可用茅坑" + sp.availablePermits()
							+ "个");
				}
			};
			service.execute(runnable);
		}
	}

}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值