Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁相关的知识,希望对你有一定的参考价值。

synchronized 是互斥锁;

lock 更广泛,包含了读写锁

读写锁特点:

1)多个读者可以同时进行读
2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

 

互斥锁特点:

  一次只能一个线程拥有互斥锁,其他线程只有等待

 

所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:

ynchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放. 而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能. 以下代码演示了在不同的块结构中获取和释放锁:

public class LockTest {
    private static Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock();
        invokeMethod();
    }

    private static void invokeMethod() {
        lock.unlock();
    }
}

为了确保锁被释放, 通常会采用如下的代码形式:

Lock lock = new ReentrantLock();
// 获取锁
lock.lock();
    try {
        // access the resource protected by this lock
    } finally {
        // 释放锁
        lock.unlock();
    }

|--void lock(): 执行此方法时, 如果锁处于空闲状态, 当前线程将获取到锁. 相反, 如果锁已经被其他线程持有, 将禁用当前线程, 直到当前线程获取到锁.

|--void unlock(): 执行此方法时, 当前线程将释放持有的锁. 锁只能由持有者释放, 如果线程并不持有锁, 却执行该方法, 可能导致异常的发生.

  • Lock提供了一个非块结构的获取锁尝试--tryLock(), 一个获取可中断锁的尝试--lockInterruptibly()和一个获取超时失效锁的尝试--tryLock(long time, TimeUnit unit).

|--boolean tryLock(): 如果锁可用, 则获取锁, 并立即返回true, 否则返回false. 该方法和lock()的区别在于, tryLock()只是"试图"获取锁, 如果锁不可用, 不会导致当前线程被禁用, 当前线程仍然继续往下执行代码. 而lock()方法则是一定要获取到锁, 如果锁不可用, 就一直等待, 在未获得锁之前,当前线程并不继续向下执行. 通常采用如下的代码形式调用tryLock()方法:

   Lock lock = new ReentrantLock();
      if (lock.tryLock()) {
          try {
              // manipulate protected state
          } finally {
              lock.unlock();
          }
      } else {
          // perform alternative actions
      }

此用法可确保如果获取了锁, 则会释放锁; 如果未获取锁, 则不会试图将其释放.

读写锁--ReadWriteLock接口及其实现类ReentrantReadWriteLock

ReentrantReadWriteLock中定义了2个内部类, ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock, 分别用来代表读取锁和写入锁. ReentrantReadWriteLock对象提供了readLock()和writeLock()方法, 用于获取读取锁和写入锁. 

 

  • 读取锁允许多个reader线程同时持有, 而写入锁最多只能有一个writter线程持有.
  • 读写锁的使用场合: 读取共享数据的频率远大于修改共享数据的频率. 在上述场合下, 使用读写锁控制共享资源的访问, 可以提高并发性能.
  • 如果一个线程已经持有了写入锁, 则可以再持有读写锁. 相反, 如果一个线程已经持有了读取锁, 则在释放该读取锁之前, 不能再持有写入锁.
  • 可以调用写入锁的newCondition()方法获取与该写入锁绑定的Condition对象, 此时与普通的互斥锁并没有什么区别. 但是调用读取锁的newCondition()方法将抛出异常.使用读写锁的一个例子:
  • public class ReadWriteLockTest {
        private static ReadWriteLock lock = new ReentrantReadWriteLock();
        private static Person person = new Person("David Beckham", true);
    
        public static void main(String[] args) {
            new Thread() {
                public void run() {
                    while(true) {
                        try {
                            lock.readLock().lock();
                            System.out.print("name = " + person.getName());
                            System.out.println(", isMan = " + person.isMan());
                        } finally {
                            lock.readLock().unlock();
                        }
                    }
                };
            }.start();
            new Thread() {
                public void run() {
                    boolean state = true;
                    while(true) {
                        try {
                            lock.writeLock().lock();
                            if (state) {
                                person.setName("Lady GaGa");
                                person.setMan(false);
                                state = false;
                            } else {
                                person.setName("David Beckham");
                                person.setMan(true);
                                state = true;
                            }
                            
                        } finally {
                            lock.writeLock().unlock();
                        }
                    }
                };
            }.start();
        }
    }
    
    class Person {
        private String name;
        private boolean isMan;
    
        public Person(String name, boolean isMan) {
            this.name = name;
            this.isMan = isMan;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public boolean isMan() {
            return isMan;
        }
    
        public void setMan(boolean isMan) {
            this.isMan = isMan;
        }
    }

    通俗易懂的实验,读写锁;

以上是关于Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁的主要内容,如果未能解决你的问题,请参考以下文章

Java锁机制1.0

Java线程锁机制:synchronizedLockCondition转载

Java各种锁机制简述

Java——线程锁,死锁,等待唤醒机制

Java 并发编程线程锁机制 ( 悲观锁 | 乐观锁 | CAS 三大问题 | ABA 问题 | 循环时间长问题 | 多个共享变量原子性问题 )

线程------Java锁机制