Lock锁的使用
在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活。
观察ReentrantLock类可以发现其实现了Lock接口
public class ReentrantLock implements Lock,java.io.Serializable
1、使用ReentrantLock实现同步
lock()方法:上锁
unlock()方法:释放锁
- /*
- * 使用ReentrantLock类实现同步
- * */
- class MyReenrantLock implements Runnable{
- //向上转型
- private Lock lock = new ReentrantLock();
- public void run() {
- //上锁
- lock.lock();
- for(int i = 0; i < 5; i++) {
- System.out.println("当前线程名: "+ Thread.currentThread().getName()+" ,i = "+i);
- }
- //释放锁
- lock.unlock();
- }
- }
- public class MyLock {
- public static void main(String[] args) {
- MyReenrantLock myReenrantLock = new MyReenrantLock();
- Thread thread1 = new Thread(myReenrantLock);
- Thread thread2 = new Thread(myReenrantLock);
- Thread thread3 = new Thread(myReenrantLock);
- thread1.start();
- thread2.start();
- thread3.start();
- }
- }
由此我们可以看出,只有当当前线程打印完毕后,其他的线程才可继续打印,线程打印的数据是分组打印,因为当前线程持有锁,但线程之间的打印顺序是随机的。
即调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放再次争抢。
2、使用Condition实现等待/通知
synchronized关键字结合wait()和notify()及notifyAll()方法的使用可以实现线程的等待与通知模式。在使用notify()、notifyAll()方法进行通知时,被通知的线程是JVM随机选择的。
类ReentrantLock类同样可以实现该功能,需要借助Condition对象,可实现“选择性通知”。Condition类是jdk1.5提供的,且在一个Lock对象中可以创建多个Condition(对象监视器)实例。
Condition类的await():是当前执行任务的线程处于等待状态
- /*
- * 错误的使用Condition实现等待、通知
- * */
- class MyCondition implements Runnable{
- private Lock lock = new ReentrantLock();
- public Condition condition = lock.newCondition();
- public void run() {
- try {
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
- //线程等待
- condition.await();
- System.out.println("我陷入了等待...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- MyCondition myCondition = new MyCondition();
- Thread thread1 = new Thread(myCondition,"线程1");
- thread1.start();
- }
- }
观察运行结果可以发现,报出监视器出错的异常,解决的办法是我们必须在condition.await()方法调用前用lock.lock()代码获得同步监视器。对上述代码做出如下修改:
- /*
- * 使用Condition实现等待
- * */
- class MyCondition implements Runnable{
- private Lock lock = new ReentrantLock();
- public Condition condition = lock.newCondition();
- public void run() {
- try {
- //上锁
- lock.lock();
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
- //线程等待
- condition.await();
- System.out.println("我陷入了等待...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- //释放锁
- lock.unlock();
- System.out.println("锁释放了!");
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- MyCondition myCondition = new MyCondition();
- Thread thread1 = new Thread(myCondition,"线程1");
- thread1.start();
- }
- }
在控制台只打印出一句,原因是调用了Condition对象的await()方法,是的当前执行任务的线程进入等待状态。
Condition类的signal():是当前执行任务的线程处于等待状态
- /*
- * 使用Condition实现等待、通知
- * */
- class MyCondition implements Runnable{
- private Lock lock = new ReentrantLock();
- public Condition condition = lock.newCondition();
- public void run() {
- try {
- //上锁
- lock.lock();
- System.out.println(" 开始等待时间:"+System.currentTimeMillis());
- System.out.println("我陷入了等待...");
- //线程等待
- condition.await();
- //释放锁
- lock.unlock();
- System.out.println("锁释放了!");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- //通知方法
- public void signal(){
- try {
- lock.lock();
- System.out.println("结束等待时间:"+System.currentTimeMillis());
- //通知等待线程
- condition.signal();
- } finally {
- lock.unlock();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) throws InterruptedException {
- MyCondition myCondition = new MyCondition();
- Thread thread1 = new Thread(myCondition,"线程1");
- thread1.start();
- Thread.sleep(3000);
- myCondition.signal();
- }
- }
观察结果我们成功地实现了等待通知。
可以得知:Object类中的wait()方法等同于Condition类中的await()方法。
Object类中的wait(long timeout)方法等同于Condition类中的await(long time,TimeUnit unit)方法。
Object类中的notify()方法等同于Condition类中的singal()方法。
Object类中的notifyAll()方法等同于Condition类中的singalAll()方法。
3、生产者消费者模式
- /*
- * 生产者、消费者模式
- * 一对一交替打印
- * */
- class MyServer{
- private ReentrantLock lock = new ReentrantLock();
- public Condition condition = lock.newCondition();
- public Boolean flag = false;
- public void set() {
- try {
- lock.lock();
- while(flag == true) {
- condition.await();
- }
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
- flag = true;
- condition.signal();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- lock.unlock();
- }
- }
- public void get() {
- try {
- lock.lock();
- while(flag == false) {
- condition.await();
- }
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
- flag = false;
- condition.signal();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- lock.unlock();
- }
- }
- }
- class MyCondition1 extends Thread{
- private MyServer myServer;
- public MyCondition1(MyServer myServer) {
- super();
- this.myServer = myServer;
- }
- public void run() {
- for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
- myServer.set();
- }
- }
- }
- class MyCondition2 extends Thread{
- private MyServer myServer;
- public MyCondition2(MyServer myServer) {
- super();
- this.myServer = myServer;
- }
- public void run() {
- for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
- myServer.get();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) throws InterruptedException {
- MyServer myServer = new MyServer();
- MyCondition1 myCondition1 = new MyCondition1(myServer);
- MyCondition2 myCondition2 = new MyCondition2(myServer);
- myCondition1.start();
- myCondition2.start();
- }
- }
- /*
- * 生产者、消费者模式
- * 多对多交替打印
- * */
- class MyServer{
- private ReentrantLock lock = new ReentrantLock();
- public Condition condition = lock.newCondition();
- public Boolean flag = false;
- public void set() {
- try {
- lock.lock();
- while(flag == true) {
- System.out.println("可能会有连续的hello进行打印");
- condition.await();
- }
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
- flag = true;
- condition.signal();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- lock.unlock();
- }
- }
- public void get() {
- try {
- lock.lock();
- while(flag == false) {
- System.out.println("可能会有连续的lemon进行打印");
- condition.await();
- }
- System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
- flag = false;
- condition.signal();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- lock.unlock();
- }
- }
- }
- class MyCondition1 extends Thread{
- private MyServer myServer;
- public MyCondition1(MyServer myServer) {
- super();
- this.myServer = myServer;
- }
- public void run() {
- for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
- myServer.set();
- }
- }
- }
- class MyCondition2 extends Thread{
- private MyServer myServer;
- public MyCondition2(MyServer myServer) {
- super();
- this.myServer = myServer;
- }
- public void run() {
- for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
- myServer.get();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) throws InterruptedException {
- MyServer myServer = new MyServer();
- MyCondition1[] myCondition1 = new MyCondition1[10];
- MyCondition2[] myCondition2 = new MyCondition2[10];
- for(int i = 0; i < 10; i++) {
- myCondition1[i] = new MyCondition1(myServer);
- myCondition2[i] = new MyCondition2(myServer);
- myCondition1[i].start();
- myCondition2[i].start();
- }
- }
- }
4、公平锁与非公平锁
锁Lock分为“公平锁”和“非公平锁”。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来的进行分配的,即先来先得FIFO先进先出顺序。
非公平锁:一种获取锁的抢占机制,是随机拿到锁的,和公平锁不一样的是先来的不一定先拿到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的·。
- /*
- * 公平锁
- * */
- class MyService{
- private ReentrantLock lock;
- public MyService(boolean isFair) {
- super();
- lock = new ReentrantLock(isFair);
- }
- public void serviceMethod() {
- try {
- lock.lock();
- System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
- } finally {
- lock.unlock();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- //设置当前为true公平锁
- final MyService myService = new MyService(true);
- Runnable runnable = new Runnable() {
- public void run() {
- System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
- myService.serviceMethod();
- }
- };
- Thread[] threads = new Thread[10];
- for(int i = 0;i < 10; i++) {
- threads[i] = new Thread(runnable);
- }
- for(int i = 0;i < 10; i++) {
- threads[i].start();
- }
- }
- }
由打印结果可以看出,基本呈现有序的状态,这就是公平锁的特点。
- /*
- * 非公平锁
- * */
- class MyService{
- private ReentrantLock lock;
- public MyService(boolean isFair) {
- super();
- lock = new ReentrantLock(isFair);
- }
- public void serviceMethod() {
- try {
- lock.lock();
- System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
- } finally {
- lock.unlock();
- }
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- //设置当前为true公平锁
- final MyService myService = new MyService(false);
- Runnable runnable = new Runnable() {
- public void run() {
- System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
- myService.serviceMethod();
- }
- };
- Thread[] threads = new Thread[10];
- for(int i = 0;i < 10; i++) {
- threads[i] = new Thread(runnable);
- }
- for(int i = 0;i < 10; i++) {
- threads[i].start();
- }
- }
- }
非公平锁的运行结果基本都是无须的,则可以表明先start()启动的线程并不一定先获得锁。
5、使用ReentrantReadWriteLock类
类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后的任务。这样虽然保证了实例变量的线程安全性,但是效率低下。所以在Java中提供有读写锁ReentrantReadWriteLock类,使其效率可以加快。在某些不需要操作实例变量的方法中,完全可以使用ReentrantReadWriteLock来提升该方法代码运行速度。
读写锁表示两个锁:
读操作相关的锁,也成为共享锁。
写操作相关的锁,也叫排他锁。
多个读锁之间不互斥,读锁与写锁互斥,多个写锁互斥。
在没有线程Thread进行写入操作时,进行读操作的多个Thread可以获取读锁,但是进行写入操作时的Thread只有获取写锁后才能进行写入操作。
(1)多个读锁共享
- /*
- * 多个读锁共享
- * */
- class MyService{
- private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- public void read() {
- try {
- //读锁
- lock.readLock().lock();
- System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁" );
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- //释放读锁
- lock.readLock().unlock();
- }
- }
- }
- //线程1
- class Thread1 extends Thread{
- private MyService myService;
- public Thread1(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.read();
- }
- }
- //线程2
- class Thread2 extends Thread{
- private MyService myService;
- public Thread2(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.read();
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- MyService myService = new MyService();
- Thread1 thread1 = new Thread1(myService);
- Thread2 thread2 = new Thread2(myService);
- thread1.start();
- thread2.start();
-
- }
- }
从打印结果可以看出,两个线程几乎同时进入lock()方法后面的代码。
说明在此时使用lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后的代码。
(2)多个写锁互斥
- /*
- * 多个写锁互斥
- * */
- class MyService{
- private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- public void write() {
- try {
- //写锁
- lock.writeLock().lock();
- System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- //释放写锁
- lock.writeLock().unlock();
- }
- }
- }
- //线程1
- class Thread1 extends Thread{
- private MyService myService;
- public Thread1(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.write();
- }
- }
- //线程2
- class Thread2 extends Thread{
- private MyService myService;
- public Thread2(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.write();
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- MyService myService = new MyService();
- Thread1 thread1 = new Thread1(myService);
- Thread2 thread2 = new Thread2(myService);
- thread1.start();
- thread2.start();
-
- }
- }
使用写锁代码writeLock.lock()的效果就是同一时间只允许一个线程执行lock()方法后的代码。
(3)读写/写读互斥
- /*
- * 读写/写读互斥,
- * */
- class MyService{
- private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- public void read() {
- try {
- //读锁
- lock.readLock().lock();
- System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁,获得时间:"+System.currentTimeMillis() );
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- //释放读锁
- lock.readLock().unlock();
- }
- }
- public void write() {
- try {
- //写锁
- lock.writeLock().lock();
- System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- //释放写锁
- lock.writeLock().unlock();
- }
- }
- }
- //线程1
- class Thread1 extends Thread{
- private MyService myService;
- public Thread1(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.read();
- }
- }
- //线程2
- class Thread2 extends Thread{
- private MyService myService;
- public Thread2(MyService myService) {
- super();
- this.myService = myService;
- }
- public void run() {
- myService.write();
- }
- }
- public class MyLock{
- public static void main(String[] args) {
- MyService myService = new MyService();
- Thread1 thread1 = new Thread1(myService);
- Thread2 thread2 = new Thread2(myService);
- thread1.start();
- thread2.start();
- }
- }
此运行结果说明“读写/写读”操作是互斥的。
由此可表明:只要出现“写”操作,就是互斥的。