`
Lucas_
  • 浏览: 4797 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

Java并发编程:Lock

 
阅读更多

Java并发编程:Lock 

一.Lock

实现提供了比使用 synchronized 方法和语句更灵活、更具可伸缩性的锁定机制,可以支持多个相关的 Condition 对象。

 

 Lock&synchronized用途比较

基本语法上,ReentrantLock与synchronized很相似,它们都具备一样的线程重入特性,只是代码写法上有点区别而已,一个表现为API层面的互斥锁(Lock),一个表现为原生语法层面的互斥锁(synchronized)。ReentrantLock相对synchronized而言还是增加了一些高级功能,主要有以下三项:

1.等待可中断:当持有锁的线程长期不释放锁时,正在等待的线程可以选择放弃等待,改为处理其他事情,它对处理执行时间非常上的同步块很有帮助。而在等待由synchronized产生的互斥锁时,会一直阻塞,是不能被中断的。

 

2.可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序排队等待,而非公平锁则不保证这点,在锁释放时,任何一个等待锁的线程都有机会获得锁。synchronized中的锁时非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过构造方法ReentrantLock(ture)来要求使用公平锁。

 

3.锁可以绑定多个条件:ReentrantLock对象可以同时绑定多个Condition对象(条件变量或条件队列),而在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含条件,但如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无需这么做,只需要多次调用newCondition()方法即可。而且我们还可以通过绑定Condition对象来判断当前线程通知的是哪些线程(即与Condition对象绑定在一起的其他线程)。

    

 实现类:ReentrantLock(可重入互斥锁)、 ReentrantReadWriteLock.ReadLock、 ReentrantReadWriteLock.WriteLock

 Lock的简单的使用:

Lock lock = new ReentrantLock();//默认使用非公平锁,如果要使用公平锁,需要传入参数true  

lock.lock();  
try {  
    //To doSomething
 } catch(Exception e){
		 	
 } finally {  		//一定在finally块中释放锁,如果锁中的代码将抛出异常,锁就有可能永远得不到释放。
	 lock.unlock();        
}

 

 

二.Lock&Condition(执行条件)

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法

常用方法:await()和signal()方法

 

例子:用Lock Condition改写上一个程序程序:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次。

 

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

public class ConditionCommunication {

	public static void main(String[] args) {

		final Business bussiness = new Business();
		new Thread(new Runnable() {
			public void run() {
				for (int i = 1; i <= 50; i++) {
					bussiness.sub(i);
				}
			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			bussiness.main(i);
		}
	}

	static class Business {
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		private boolean bShouldSub = true;

		public void sub(int i) {
			lock.lock();
			try {
				while (!bShouldSub) { // 防止虚假唤醒
					try {
						condition.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				for (int j = 1; j <= 10; j++) {
					System.out.println("sub thread sequence of " + j
							+ ", loop of " + i);
				}
				bShouldSub = false;
				condition.signal();

			} finally {
				lock.unlock();
			}
		}

		public void main(int i) {
			lock.lock();
			try {
				while (bShouldSub) {
					try {
						condition.await();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 100; j++) {
					System.out.println("main thread sequence of " + j
							+ ", loop of " + i);
				}
				bShouldSub = true;
				condition.signal();

			} finally {
				lock.unlock();
			}
		}
	}
}

可以参照Condition的Api中有个阻塞队列的实例

 

需要注意Condition中的是await()方法,  不是从Object继承的wait() (用错不报错)。

 

另一个例子:

/*
	主线程循环100,子线程1循环10次,子线程2循环20次
	接着又回到主线程循环100次,接着再回到子线程1.....,如此循环50次,请写出程序。 

 */

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

public class ThreeConditionCommunication {

	public static void main(String[] args) {

		final Business bussiness = new Business();
		new Thread(new Runnable() {
			public void run() {
				for (int i = 1; i <= 50; i++) {
					bussiness.sub2(i);
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			public void run() {
				for (int i = 1; i <= 50; i++) {
					bussiness.sub3(i);
				}
			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			bussiness.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 (InterruptedException e) {
						// TODO Auto-generated catch block
						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 (InterruptedException e) {
						// TODO Auto-generated catch block
						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(InterruptedException 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();
			}
		}
	}
}

 

三.读写锁

    读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。

 正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

 ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

 

 可以通过readLock()获取读锁,通过writeLock()获取写锁。

 例子:读写锁,创建3个线程用来读 , 3个线程用来写

import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {

	public static void main(String[] args) {
		final Queue que = new Queue();
		for (int i = 0; i < 3; i++) {

			new Thread(new Runnable() {
				public void run() {
					while (true) {
						que.get();
					}
				}
			}).start();

			new Thread(new Runnable() {
				public void run() {
					while (true) {
						que.put(new Random().nextInt(10000));
					}
				}
			}).start();

		}
	}
}

class Queue {
	private Object data = null; // 需要读写的对象
	ReadWriteLock rwl = new ReentrantReadWriteLock();

	public void get() {
		rwl.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()
					+ " is ready to get data!");
			Thread.sleep(new Random().nextInt(1000));
			System.out.println(Thread.currentThread().getName()
					+ " hava read data: " + data);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rwl.readLock().unlock();
		}
	}

	public void put(Object data) {
		rwl.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()
					+ " is ready to put data!");
			Thread.sleep((long) Math.random() * 1000);
			this.data = data;
			System.out.println(Thread.currentThread().getName()
					+ " have write data " + data);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rwl.writeLock().unlock();
		}
	}

}

 

总结来说,Lock和synchronized有以下几点不同:

  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

        2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

        3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

       4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

       5)Lock可以提高多个线程进行读操作的效率。

 

当需要以下高级特性时,才应该使用:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。否则,请使用synchronized。

 

分享到:
评论

相关推荐

    Java 并发核心编程

    本文的主题是关于具有java语言风格的Thread、synchronized、volatile,以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发集合类、线程协作摘要、Executors。开发者通过这些基础的接口可以构建高并发、线程...

    Java并发编程的艺术.md

    《Java并发编程的艺术》笔记 第一章 并发编程的挑战 第二章 Java并发机制的底层实现原理 volatile的两条实现原则: 1. Lock前缀指令会引起处理器缓存回写到内存 2. 一个处理器的缓存回写到内存会导致其他...

    龙果 java并发编程原理实战

    龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...

    java高级技术JUC高并发编程教程2021(1.5G)

    07-JUC高并发编程-Lock接口概述和实现案例.mp48 G J/ u; W' _$ o: {2 M 08-JUC高并发编程-线程间通信-概述和案例分析.mp4 09-JUC高并发编程-线程间通信-Synchronized实现案例.mp4 10-JUC高并发编程-线程间通信-虚假...

    Java并发编程原理与实战

    Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读ReentrantReadWriteLock源码.mp4 ...

    Java并发编程(20)并发新特性-Lock锁和条件变量(

    Java并发编程(20)并发新特性—Lock锁和条件变量(含代码)编程开发技术共14页.pdf.zip

    龙果java并发编程完整视频

    第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23节使用AQS重写自己的锁00:31:04分钟 | 第24节重入锁原理与...

    Java 并发学习笔记:进程和线程,并发理论,并发关键字,Lock 体系,原子操作类,发容器 &amp; 并发工具,线程池,并发实践

    Java 并发学习笔记: 进程和线程, 并发理论, 并发关键字, Lock 体系, 原子操作类, 发容器 & 并发工具, 线程池, 并发实践 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的...

    Java 并发编程原理与实战视频

    java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个...

    java并发编程面试题

    java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换? 线程和进程区别 什么是线程和进程? 创建线程有哪几种方式?,如何避免线程死锁 线程的 run()和 start()有什么区别? 什么是 ...

    Java-并发(Concurrent)编程

    并发编程:编写多线程代码,解决多线程带来的问题 为什么要学并发编程? 首先,来看一个案例:手写网站服务器案例。 高性能应用程序的一把钥匙,应用程序的翅膀,面试高频的考点 中间件几乎都是多线程应用:MySQL、...

    Java 7并发编程实战手册

    java7在并发编程方面,带来了很多令人激动的新功能,这将使你的应用程序具备更好的并行任务性能。 《Java 7并发编程实战手册》是Java 7并发编程的实战指南,介绍了Java 7并发API中大部分重要而有用的机制。全书分为9...

    java 并发编程 多线程

    java 并发 编程 多线程 concurrent lock condition executorserice executor java.util.curcurrent.

    汪文君高并发编程实战视频资源下载.txt

    │ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...

    Java并发编程实战

    Java并发编程实战 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及...

    Java并发编程常见问题

    Java并发编程常见问题说明,包括Lock,synchronized,volatile等

    浅谈Java并发编程之Lock锁和条件变量

    主要介绍了浅谈Java并发编程之Lock锁和条件变量,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    JAVA高质量并发详解,多线程并发深入讲解

    接着,深入讲解了Java并发编程的核心API,如synchronized关键字、Lock接口、Condition接口、Semaphore等,帮助读者掌握Java并发编程的基本工具和方法。 除了基础知识和API的讲解,本书还重点介绍了Java并发编程的...

    2万字Java并发编程面试题合集(含答案,建议收藏)

    2万字Java并发编程面试题合集(含答案,建议收藏) 具体如下 1、在 java 中守护线程和本地线程区别?2、线程与进程的区别?3、什么是多线程中的上下文切换?4、死锁与活锁的区别,死锁与饥饿的区别?5、Java 中用到...

Global site tag (gtag.js) - Google Analytics