(六)Lock锁的使用

一 Lock接口

1.1 Lock接口简介

锁是用于通过多个线程控制对共享资源访问的工具。通常,锁提供共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求先获取锁。但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读写锁。

在Lock接口出现之前,Java程序是靠synchronized关键字实现锁的功能。JDK1。5之后并发包中新增了Lock接口以及相关实现类来实现锁的功能。

虽然synchronized方法和语句的范围机制使得 使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时你需要更加灵活的方式处理锁。

例如:用于遍历并发访问的数据结构的一些算法需要 ”手动“ 或 ”链锁定“:你获取节点A的锁定,额安后获取节点B,然后释放A并获取C,然后释放B并获取D等操作。在这种场景中synchronized关键字就不那么容易实现了,使用Lock接口就要容易很多。

Lock接口的实现类:

Reentrantlock , ReentrantReadWriteLock.ReadLock , ReentrantReadWriteLock.WriteLock

1.2 Lock的简单使用

  Lock lock=new ReentrantLock();
  lock.lock();
   try{
    }finally{
    lock.unlock();
    }

因为Lock是接口,所以使用时要结合它的实现类,另外在finally语句块中释放锁的目的是保证获取到锁之后,最终都能够被释放。

注意: 最好不要把获取锁的过程写在 try 语句块中,因为如果在获取锁时发生了异常,异常抛出的同时也会导致锁无法被释放。

1.3 Lock接口的特性和常见方法

Lock接口提供synchronized关键字不具有的特性:

特性描述
尝试非阻塞地获取锁当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
能被中断地获取锁获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
超时获取锁在指定的截止时间之前获取锁, 超过截止时间后仍旧无法获取则返回

Lock接口基本的方法:

方法名称描述
void lock()获得锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获取锁。
void lockInterruptibly()获取锁,如果可用并立即返回。如果锁不可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,和lock()方法不同的是在锁的获取中可以中断当前线程(相应中断)。
Condition newCondition()获取等待通知组件,该组件和当前的锁绑定,当前线程只有获得了锁,才能调用该组件的wait()方法,而调用后,当前线程将释放锁。
boolean tryLock()只有在调用时才可以获得锁。如果可用,则获取锁定,并立即返回值为true;如果锁不可用,则此方法将立即返回值为false 。
boolean tryLock(long time, TimeUnit unit)超时获取锁,当前线程在一下三种情况下会返回: 1. 当前线程在超时时间内获得了锁;2.当前线程在超时时间内被中断;3.超时时间结束,返回false.
void unlock()释放锁。

二 Lock接口的实现类:ReentrantLock

ReentrantLocksynchronized关键字一样可以用来实现线程之间的同步互斥,但是在功能上Reentrant比synchronized关键字更强大而且更灵活。

ReentrantLock类常见方法:

构造方法:

方法名称描述
ReentrantLock()创建一个 ReentrantLock的实例。
ReentrantLock(boolean fair)创建一个特定锁类型(公平锁/非公平锁)的ReentrantLock的实例

ReentrantLock类常见方法(Lock接口已有方法这里没加上):

方法名称描述
int getHoldCount()查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
protected Thread getOwner()返回当前拥有此锁的线程,如果不拥有,则返回 null
protected Collection getQueuedThreads()返回包含可能正在等待获取此锁的线程的集合
int getQueueLength()返回等待获取此锁的线程数的估计。
protected Collection getWaitingThreads(Condition condition)返回包含可能在与此锁相关联的给定条件下等待的线程的集合。
int getWaitQueueLength(Condition condition)返回与此锁相关联的给定条件等待的线程数的估计。
boolean hasQueuedThread(Thread thread)查询给定线程是否等待获取此锁。
boolean hasQueuedThreads()查询是否有线程正在等待获取此锁。
boolean hasWaiters(Condition condition)查询任何线程是否等待与此锁相关联的给定条件
boolean isFair()如果此锁的公平设置为true,则返回 true 。
boolean isHeldByCurrentThread()查询此锁是否由当前线程持有。
boolean isLocked()查询此锁是否由任何线程持有。

2.1 第一个ReentrantLock程序

ReentrantLockTest.java

public class ReentrantLockTest {

    public static void main(String[] args) {

        MyService service = new MyService();

        MyThread a1 = new MyThread(service);
        MyThread a2 = new MyThread(service);
        MyThread a3 = new MyThread(service);
        MyThread a4 = new MyThread(service);
        MyThread a5 = new MyThread(service);

        a1.start();
        a2.start();
        a3.start();
        a4.start();
        a5.start();

    }

    static public class MyService {

        private Lock lock = new ReentrantLock();

        public void testMethod() {
            lock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
                }
            } finally {
                lock.unlock();
            }

        }

    }

    static public class MyThread extends Thread {

        private MyService service;

        public MyThread(MyService service) {
            super();
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }
}

运行结果:

ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5

从运行结果可以看出,当一个线程运行完毕后才把锁释放,其他线程才能执行,其他线程的执行顺序时不确定的。

2.2 Condition接口简介

我们通过之前的学习知道了:synchronized关键字与wait()和notify()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助与Condition接口与newCondition()方法。Condition时JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活

在使用notify()/notifyAll()方法进行通知时,被通知的线程是有JVM选择的,使用ReentrantLock类结合Condition实例可以实现”选择性通知“,这个功能非常重要,而且是Condition接口默认提供的。

而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话,就会通知所有处于等待状态的线程,这样就会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。

Condition接口的常见方法:

方法名称描述
void await()相当于Object类的wait方法
boolean await(long time, TimeUnit unit)相当于Object类的wait(long timeout)方法
signal()相当于Object类的notify方法
signalAll()相当于Object类的notifyAll方法

2.3 使用Condition实现等待/通知机制

  1. 使用单个Condition实现等待/通知机制:

    UseSingleConditionWaitNotify.java

    public class UseSingleConditionWaitNotify {
    
        public static void main(String[] args) throws InterruptedException {
    
            MyService service = new MyService();
    
            ThreadA a = new ThreadA(service);
            a.start();
    
            Thread.sleep(3000);
    
            service.signal();
    
        }
    
        static public class MyService {
    
            private Lock lock = new ReentrantLock();
            public Condition condition = lock.newCondition();
    
            public void await() {
                lock.lock();
                try {
                    System.out.println(" await时间为" + System.currentTimeMillis());
                    condition.await();
                    System.out.println("这是condition.await()方法之后的语句,condition.signal()方法之后我才被执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
    
            public void signal() throws InterruptedException {
                lock.lock();
                try {               
                    System.out.println("signal时间为" + System.currentTimeMillis());
                    condition.signal();
                    Thread.sleep(3000);
                    System.out.println("这是condition.signal()方法之后的语句");
                } finally {
                    lock.unlock();
                }
            }
        }
    
        static public class ThreadA extends Thread {
    
            private MyService service;
    
            public ThreadA(MyService service) {
                super();
                this.service = service;
            }
    
            @Override
            public void run() {
                service.await();
            }
        }
    }
    

    运行结果:

     await时间为1572518314968
    signal时间为1572518317967
    这是condition.signal()方法之后的语句
    这是condition.await()方法之后的语句,condition.signal()方法之后我才被执行
    

    condition.signal()相当于notify()方法,不会立即释放锁。所以signal()方法会直接执行完。

    在使用wait/notify实现等待/通知机制的时候,我们知道必须执行完notify()方法所在的synchronized代码块后才会释放锁。在里也差不多,必须执行完signal语句所在的try语句块之后才释放锁,condition.await()后的语句才能被执行

    注意: 必须在condition.await()方法调用之前调用lock.lock()代码块获得同步监视器,不然会报错。

  2. 使用多个Condition实现等待/通知机制:

    UseMoreConditionWaitNotify.java

    public class UseMoreConditionWaitNotify {
        public static void main(String[] args) throws InterruptedException {
    
            MyserviceMoreCondition service = new MyserviceMoreCondition();
    
            ThreadA a = new ThreadA(service);
            a.setName("A");
            a.start();
    
            ThreadB b = new ThreadB(service);
            b.setName("B");
            b.start();
    
            Thread.sleep(3000);
    
            service.signalAll_A();
    
        }
        static public class ThreadA extends Thread {
    
            private MyserviceMoreCondition service;
    
            public ThreadA(MyserviceMoreCondition service) {
                super();
                this.service = service;
            }
    
            @Override
            public void run() {
                service.awaitA();
            }
        }
        static public class ThreadB extends Thread {
    
            private MyserviceMoreCondition service;
    
            public ThreadB(MyserviceMoreCondition service) {
                super();
                this.service = service;
            }
    
            @Override
            public void run() {
                service.awaitB();
            }
        }
    
    }
    

    MyserviceMoreCondition.java

    public class MyserviceMoreCondition {
    
        private Lock lock = new ReentrantLock();
        public Condition conditionA = lock.newCondition();
        public Condition conditionB = lock.newCondition();
    
        public void awaitA() {
            lock.lock();
            try {
                System.out.println("begin awaitA时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
                conditionA.await();
                System.out.println("  end awaitA时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void awaitB() {
            lock.lock();
            try {           
                System.out.println("begin awaitB时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
                conditionB.await();
                System.out.println("  end awaitB时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalAll_A() {
            lock.lock();
            try {           
                System.out.println("  signalAll_A时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
                conditionA.signalAll();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalAll_B() {
            lock.lock();
            try {       
                System.out.println("  signalAll_B时间为" + System.currentTimeMillis()
                        + " ThreadName=" + Thread.currentThread().getName());
                conditionB.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }
    

    运行结果:

    begin awaitA时间为1572519137865 ThreadName=A
    begin awaitB时间为1572519137865 ThreadName=B
      signalAll_A时间为1572519140865 ThreadName=main
      end awaitA时间为1572519140865 ThreadName=A
      程序未终止
    

    只有A线程被唤醒了。

  3. 使用Condition实现顺序执行:

    ConditionSeqExec.java

    public class ConditionSeqExec {
    
        volatile private static int nextPrintWho = 1;
        private static ReentrantLock lock = new ReentrantLock();
        final private static Condition conditionA = lock.newCondition();
        final private static Condition conditionB = lock.newCondition();
        final private static Condition conditionC = lock.newCondition();
    
        public static void main(String[] args) {
    
            Thread threadA = new Thread() {
                public void run() {
                    try {
                        lock.lock();
                        while (nextPrintWho != 1) {
                            conditionA.await();
                        }
                        for (int i = 0; i < 3; i++) {
                            System.out.println("ThreadA " + (i + 1));
                        }
                        nextPrintWho = 2;
                        //通知conditionB实例的线程运行
                        conditionB.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread threadB = new Thread() {
                public void run() {
                    try {
                        lock.lock();
                        while (nextPrintWho != 2) {
                            conditionB.await();
                        }
                        for (int i = 0; i < 3; i++) {
                            System.out.println("ThreadB " + (i + 1));
                        }
                        nextPrintWho = 3;
                        //通知conditionC实例的线程运行
                        conditionC.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread threadC = new Thread() {
                public void run() {
                    try {
                        lock.lock();
                        while (nextPrintWho != 3) {
                            conditionC.await();
                        }
                        for (int i = 0; i < 3; i++) {
                            System.out.println("ThreadC " + (i + 1));
                        }
                        nextPrintWho = 1;
                        //通知conditionA实例的线程运行
                        conditionA.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
            Thread[] aArray = new Thread[5];
            Thread[] bArray = new Thread[5];
            Thread[] cArray = new Thread[5];
    
            for (int i = 0; i < 5; i++) {
                aArray[i] = new Thread(threadA);
                bArray[i] = new Thread(threadB);
                cArray[i] = new Thread(threadC);
    
                aArray[i].start();
                bArray[i].start();
                cArray[i].start();
            }
    
        }
    }
    

    运行结果:

    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1
    ThreadC 2
    ThreadC 3
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1
    ThreadC 2
    ThreadC 3
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1
    ThreadC 2
    ThreadC 3
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1
    ThreadC 2
    ThreadC 3
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1
    ThreadC 2
    ThreadC 3
    

    通过代码很好理解,说简单点就是在一个线程运行完之后通过condition.signal()/condition.signalAll()方法通知下一个特定的线程运行,顺序循环运行5次。

    注意: 默认情况下ReentrantLock类使用的是非公平锁。

    1private static ReentrantLock lock = new ReentrantLock();
    
    2public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    3/**
      * Sync object for non-fair locks
    */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
    
        /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

2.4 公平锁与非公平锁

Lock锁分为:公平锁非公平锁。公平锁表示线程获取锁的顺序是按照线程枷锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先获取到锁,这样可能造成某些线程一直拿不到锁,结果也就是不公平的了。

FairorNofairLock.java

public class FairorNofairLock {

    public static void main(String[] args) throws InterruptedException {
        final Service service = new Service(true);//true为公平锁,false为非公平锁

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("★线程" + Thread.currentThread().getName()
                        + "运行了");
                service.serviceMethod();
            }
        };

        Thread[] threadArray = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threadArray[i] = new Thread(runnable);
        }
        for (int i = 0; i < 10; i++) {
            threadArray[i].start();
        }

    }
    static public class Service {

        private ReentrantLock lock;

        public Service(boolean isFair) {
            super();
            lock = new ReentrantLock(isFair);
        }

        public void serviceMethod() {
            lock.lock();
            try {
                System.out.println("ThreadName=" + Thread.currentThread().getName()
                        + "获得锁定");
            } finally {
                lock.unlock();
            }
        }

    }
}

运行结果:

★线程Thread-1运行了
★线程Thread-3运行了
★线程Thread-0运行了
★线程Thread-2运行了
★线程Thread-7运行了
★线程Thread-4运行了
ThreadName=Thread-1获得锁定
★线程Thread-8运行了
★线程Thread-5运行了
ThreadName=Thread-3获得锁定
★线程Thread-6运行了
ThreadName=Thread-0获得锁定
ThreadName=Thread-2获得锁定
★线程Thread-9运行了
ThreadName=Thread-7获得锁定
ThreadName=Thread-4获得锁定
ThreadName=Thread-8获得锁定
ThreadName=Thread-5获得锁定
ThreadName=Thread-6获得锁定
ThreadName=Thread-9获得锁定

公平锁的运行结果是有序的。

把Service的参数修改为false则为非公平锁

final Service service = new Service(false);//true为公平锁,false为非公平锁

运行结果:

★线程Thread-0运行了
★线程Thread-1运行了
ThreadName=Thread-0获得锁定
ThreadName=Thread-1获得锁定
★线程Thread-5运行了
★线程Thread-4运行了
ThreadName=Thread-5获得锁定
★线程Thread-2运行了
★线程Thread-3运行了
ThreadName=Thread-2获得锁定
★线程Thread-6运行了
ThreadName=Thread-6获得锁定
★线程Thread-8运行了
ThreadName=Thread-8获得锁定
ThreadName=Thread-4获得锁定
★线程Thread-9运行了
ThreadName=Thread-9获得锁定
ThreadName=Thread-3获得锁定
★线程Thread-7运行了
ThreadName=Thread-7获得锁定

非公平锁的运行结果是无序的。

三 ReadWriteLock接口的实现类:ReentrantReadWriteLock

3.1 简介

我们刚刚接触到的ReentrantLock排他锁)具有完全互斥排他的效果,即同意时刻只允许一个线程访问,这样做瑞然保证了实例变量的线程安全,但是效率非常低。ReadWriteLock接口的实现类—ReentrantReadWriteLock读写锁就是为了解决这个问题。

读写锁维护了两个锁,一个是读操作相关的锁  也称为共享锁,一个是写操作相关的锁  也称为排他锁。通过分离读锁和写锁,其并发性比一般排他锁有了很大提升。

多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥(主要出现写操作的过程就是互斥的)

在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。即多个thread可以同时进行读取操作,但是同一时刻只允许一个thread进行写入操作。

3.2 ReentrantReadWriteLock的特性与常见方法

ReentrantReadWriteLock的特性:

特性说明
公平性选择支持非公平(默认)和公平的锁获取方式,吞吐量上来看还是非公平优于公平
重进入该锁支持重进入,以读写线程为例:读线程在获取了读锁之后,能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁也能够同时获取读锁
锁降级遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级称为读锁

ReentrantReadWriteLock常见方法:
构造方法

方法名称描述
ReentrantReadWriteLock()创建一个 ReentrantReadWriteLock()的实例
ReentrantReadWriteLock(boolean fair)创建一个特定锁类型(公平锁/非公平锁)的ReentrantReadWriteLock的实例

常见方法:
和ReentrantLock类 类似这里就不列举了。

3.3 ReentrantReadWriteLock的使用

  1. 读读共享

    两个线程同时运行read方法,你会发现两个线程可以同时或者说几乎同时运行lock()方法后面的代码,输出的两句话显示的时间一样。这样提高了程序的运行效率。

    public class Service {
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void read() {
            try {
                try {
                    lock.readLock().lock();
                    System.out.println("获得读锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.readLock().unlock();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    
    public class ThreadA extends Thread {
    
        private Service service;
    
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    }
    
    public class ThreadB extends Thread {
    
        private Service service;
    
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    }
    
    public class Run {
    
        public static void main(String[] args) {
            Service service = new Service();
    
            ThreadA threadA = new ThreadA(service);
            ThreadB threadB = new ThreadB(service);
    
            threadA.start();
            threadB.start();
        }
    }
    

    运行结果:

    获得读锁Thread-0 1572525181219
    获得读锁Thread-1 1572525181219
    

    几乎在同一时刻输出。

  2. 写写互斥

    把上面的代码中

    lock.readLock().lock();
    lock.readLock().unlock();
    

    改为:

    lock.writeLock().lock();
    lock.writeLock().unlock();
    

    运行结果:

    获得读锁Thread-0 1572525378669
    等待10秒
    获得读锁Thread-1 1572525388671
    

    两个线程同时运行read方法,你会发现同一时刻只允许一个线程执行lock()方法后面的代码。

  3. 读写互斥

    public class Service {
    
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void read() {
            try {
                try {
                    lock.readLock().lock();
                    System.out.println("获得读锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.readLock().unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void write() {
            try {
                try {
                    lock.writeLock().lock();
                    System.out.println("获得写锁" + Thread.currentThread().getName()
                            + " " + System.currentTimeMillis());
                    Thread.sleep(10000);
                } finally {
                    lock.writeLock().unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    
    }
    
    
    
    public class ThreadA extends Thread {
    
        private Service service;
    
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.read();
        }
    
    }
    
    
    
    public class ThreadB extends Thread {
    
        private Service service;
    
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.write();
        }
    
    }
    
    
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            Service service = new Service();
    
            ThreadA threadA = new ThreadA(service);
            threadA.setName("A");
            threadA.start();
    
            Thread.sleep(1000);
    
            ThreadB threadB = new ThreadB(service);
            threadB.setName("B");
            threadB.start();
        }
    
    }
    
    

    运行结果:

    获得读锁A 1572525637084
    等待10秒输出
    获得写锁B 1572525647085
    等待10秒程序结束
    

    运行两个使用同一个Service对象实例的线程a、b,线程a执行service.read();方法,线程b执行service.write();方法。你会发现同一时刻只允许一个线程执行lock()方法后面的代码。

    记住:只要出现写操作的过程就是互斥的。

  4. 写读互斥

    和读写互斥类似,这里不用代码演示了。

    记住:只要出现写操作的过程就是互斥的。

参考:

《Java多线程编程核心技术》

《Java并发编程的艺术》

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值