Java并发编程原理(五)

LK 接着上面的内容继续总结相关知识,新的一年里希望自己能够持续更新自己的技术栈,也祝愿各位浏览我主页的朋友新的一年升职加薪。

公平锁

1.为什么要有公平锁?

是因为出现了不公平--java中称之为饥饿。

> 如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,这种状态被称之为“饥饿”。

总结:是因为自己的cpu运行时间被其他线程占用了。

2.Java中导致饥饿的原因

  • 高优先级线程吞噬所有的低优先级线程的CPU时间。(线程优先级)
  • 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。(Java的同步代码区对哪个线程允许进入的次序没有任何保障)
  • 线程在等待一个本身(在其上调用wait())也处于永久等待完成的对象,因为其他线程总是被持续地获得唤醒。(多个线程调用wait()方法,使用nofity方法唤醒了其中不确定的一个,总有一个没被唤醒)

公平锁原理

在这里插入图片描述

public class QueueObject {
	private boolean flag=false;
	public synchronized void doWait() throws InterruptedException {
		//自旋,
		while (!flag) {
			this.wait();
		}
		flag=false;
	}
	
	public synchronized void doNotify() {
		flag=true;
		this.notify();
	}
	
	@Override
	public boolean equals(Object obj) {
		return this==obj;
	}
}

public class FairLock {
	// private boolean flag=false;
	private Thread currenThread = null;
	List<QueueObject> queueList = new ArrayList<QueueObject>();

	private void MyLock() {
		QueueObject objectQueue = new QueueObject();
		currenThread = Thread.currentThread();
		synchronized (this) {
			queueList.add(objectQueue);
		}
		try {
			objectQueue.doWait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			synchronized (this) {
				queueList.remove(objectQueue);
			}
		}

	}

	private void MyUnlock() {
		if (currenThread != Thread.currentThread())
			throw new IllegalMonitorStateException("Calling thread has not locked this lock");
		else
			currenThread = null;
		if (queueList.size() > 0) {
			queueList.get(0).doNotify();
		}
	}

}

主要是内部维护了一个队列,第一个线程在队列中添加完一个元素后,其他线程添加完元素会自旋阻塞,等待队列头部元素唤醒。下一个元素进入,排队机制可见应用还是相当广泛的。

读写锁

1.什么是读写锁?
读写锁是一种特殊的自旋锁,它把对共享资源对访问者划分成了读者和写者,读者只对共享资源进行访问写者则是对共享资源进行写操作
读写锁在同一时刻可以
允许多个读线程访问
,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升
2.使用场景?
读操作多于写操作时
3.简单实例

public class MyReadWriteLock {


		private ReadWriteLock rwl = new ReentrantReadWriteLock();

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

		public void setMap(String string, Object object) {
			rwl.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
			try {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				map.put(string, object);
			} finally {
				rwl.writeLock().unlock();
				System.out.println(Thread.currentThread().getName() + " 写操作执行完毕");
			}
		}

		public Object getMap(String key) {
			rwl.readLock().lock();
			System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
			try {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return map.get(key);

			} finally {
				rwl.readLock().unlock();
				System.out.println(Thread.currentThread().getName() + " 读操作执行完毕");
			}

		}
	 
}

	public static void main(String args[]) {
		
		MyReadWriteLock lock=new MyReadWriteLock();
		lock.setMap("aa", "xxxxx");
		ExecutorService service = Executors.newFixedThreadPool(20);
		for (int i = 0; i < 20; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(
							Thread.currentThread().getName() + "..." +lock.getMap("aa"));
				}
			});
		}

	}

4.读写锁释放和获取
总结: 每次释放均减少写状态,当写状态为0时表示写锁已被释放,从而等待的读写线程能够继续访问读写锁,同时前次写线程的修改对后续读写线程可见。
读锁的每次释放均(线程安全的,可能有多个读线程同时释放读锁)减少读状态
之后在分析AQS时再探讨
5.锁降级
过程:获取写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程
目的:自我感悟是减少写锁的持有,提高性能。

public class LockDown {

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

   private ReadWriteLock rwl = new ReentrantReadWriteLock();

   private Lock r = rwl.readLock();
   private Lock w = rwl.writeLock();

   private volatile boolean isUpdate = false;

   public void readWrite() {
   	r.lock(); // 为了保证isUpdate能够拿到最新的值
   	if (!isUpdate) {
   		r.unlock();
   		w.lock();
   		try {
   			if (!isUpdate) {
   				// 准备数据的
   				map.put("asdsaf", "12314");
   			}
   			r.lock();// 获取读锁,保证数据的可见性。
   			// 直接释放写锁,会导致其他线程对数据进行跟新但是当前线程无法感知
   			// 获取读锁后其他线程会被阻塞,直到当前线程使用数据后,其它线程才可以获取。
   		} finally {
   			w.unlock();
   		}
   	}

   	try {
   		// 使用数据
   		Object obj = map.get("asdsaf");

   		System.out.println(obj);
   	} finally {
   		r.unlock();
   	}

   }

   public Object get(String key) {
   	r.lock();
   	System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
   	try {
   		try {
   			Thread.sleep(3000);
   		} catch (InterruptedException e) {
   			e.printStackTrace();
   		}
   		return map.get(key);
   	} finally {
   		r.unlock();
   		System.out.println(Thread.currentThread().getName() + " 读操执行完毕..");
   	}
   }

   public void put(String key, Object value) {
   	w.lock();
   	System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
   	try {
   		try {
   			Thread.sleep(3000);
   		} catch (InterruptedException e) {
   			e.printStackTrace();
   		}
   		map.put(key, value);
   	} finally {
   		w.unlock();
   		System.out.println(Thread.currentThread().getName() + " 写操作执行完毕..");
   	}
   }

   public static void main(String[] args) {
   	LockDown lockDown = new LockDown();
   	new Thread(new Runnable() {
   		@Override
   		public void run() {
   			lockDown.readWrite();
   		}
   	}).start();
   	new Thread(new Runnable() {
   		@Override
   		public void run() {
   			lockDown.readWrite();
   		}
   	}).start();
   	new Thread(new Runnable() {
   		@Override
   		public void run() {
   			lockDown.readWrite();
   		}
   	}).start();
   	new Thread(new Runnable() {
   		@Override
   		public void run() {
   			lockDown.readWrite();
   		}
   	}).start();
   	 
   }

}
发布了47 篇原创文章 · 获赞 18 · 访问量 5725

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/87103534