• 引言
  • ReentrantLock类的使用
  • 1 使用ReentrantLock进行加锁
  • 11 代码实现
  • 12 注意事项
  • 2 ReentrantLock类实现等待通知机制
  • 21 代码实现
  • 22注意事项
  • ReentrantReadWriteLock的使用
  • 1 使用ReentrantReadWriteLock对象实现共享
  • 11 代码实现
  • 12注意事项
  • 2 ReentrantReadWriteLock类实现互斥现象
  • 21 读写互斥现象写读互斥
  • 22 写写互斥现象
  • 总结


1.引言

      在前面的博客中,要想实现线程之间的同步,可以通过添加synchronized关键字来实现,在JDK5以后,出现了一个接口叫做Lock,也就是说:在JDK5以后,将锁也定义成了对象 ,并且锁对象所实现的功能比synchronized关键字更加强大。在此篇博客中,主要介绍两个类的使用ReentrantLockReentrantReadWriteLock

2.ReentrantLock类的使用

2.1 使用ReentrantLock进行加锁

2.1.1 代码实现

      我们定义一个service类,类中有一个方法(此方法是同步方法)。

  • service类
public class Service {
    private Lock lock=new ReentrantLock();
    public void printString()
    {
        lock.lock();
        System.out.println("");
        lock.unlock();
    }

}
  • 在线程类中的使用
public class ThreadA extends Thread{
    private Service service;
    public ThreadA(Service service) {
        this.service=service;
    }
    public void run() {
        service.printString();

    }

}
  • main函数
public class app {
    public static void main(String[] args) {    
        Service service=new Service();
        ThreadA a=new ThreadA(service);
        ThreadA b=new ThreadA(service);
        ThreadA c=new ThreadA(service);
        ThreadA d=new ThreadA(service);
        a.start();
        b.start();
        c.start();
        d.start();
    }

}

2.1.2 注意事项

  • 如果我们使用synchronized关键字,那么进入块之前要自动获取锁,执行完块代码,程序将会自动释放锁。
  • 如果使用Lock实现加锁机制,那么通过lock.lock()方法加锁,通过lock.unlock()方法释放锁(注意这里我们是通过代码人为的释放锁)

2.2 ReentrantLock类实现等待通知机制

      在前面如果我们想要实现等待/通知机制的话。需要注意一点,就是首先要给代码加锁首先要给代码加锁,首先要给代码加锁。在Lock对象中也是如此。在本实例中,同样实现一个功能:线程B负责给数组arr添加数据,线程A监控数组arr,如果数组arr的size属性为5的时候,线程A输入数组的内容。(注意:要加锁

2.2.1 代码实现

  • 线程A的代码
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class MyThreadA extends Thread {
    private List arr;
    private Lock lock;
    private Condition condition;

    public MyThreadA(List arr, Lock lock, Condition condition) {
        this.arr = arr;
        this.lock = lock;
        this.condition = condition;
    }

    public void run() {
        lock.lock();
        try {
            if (arr.size() != 5) {
                System.out.println("arr的大小不等于5,我要等待了~");
                // 线程等待,一直等待到被现成B唤醒
                condition.await();              
            }
            System.out.println("arr的大小等于5了");
            System.out.println("arr的内容为:" + arr.toString());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
    }

}
  • 线程B的代码
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class MyThreadB extends Thread {
    private List arr;
    private Lock lock;
    private Condition condition;

    public MyThreadB(List arr, Lock lock, Condition condition) {
        this.arr = arr;
        this.lock = lock;
        this.condition = condition;
    }

    public void run() {         
            lock.lock();
            int i = 0;
            while (true) {
                try {           
                    arr.add(i);
                    i++;
                    if (arr.size() == 5) {
                        // 唤醒线程a
                        condition.signal();
                        break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            lock.unlock();
    }

}
  • 运行结果

Java 接口数据加密传输 java接口加锁_java

2.2.2注意事项

  • 注意和synchronized关键字区分,在synchronized关键字中,如果想要唤醒线程,使用notify()方法和notifyAll()方法
  • Lock对象中,想要唤醒线程是使用signal()方法和signalAll()方法
  • 注意下图,等待通知机制中synchronizedlock的区别,对于lock对象添加了一个condition对象

Java 接口数据加密传输 java接口加锁_多线程_02

  • 注意:synchronized关键字,陷入等待使用的是wait()方法,而lock对象想要陷入等待,使用的是await()方法

3.ReentrantReadWriteLock的使用

      类ReentrantLock具有完全排他的效果,这样虽然保证了线程的安全性,也降低了效率,在某些情况下,我们既想保证安全性,还想保证一定的效率,那么应该怎么办呢?在什么需求当中会出现这种情况呢?比如:多个线程只想读取数组arr的内容,并不修改arr的内容。这样我们既想保证安全性,又要提高效率,就要用到ReentrantReadWriteLock对象来实现。

3.1 使用ReentrantReadWriteLock对象实现共享

3.1.1 代码实现

  • Service方法
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class Service {
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
    public void printString() throws Exception
    {
        lock.readLock().lock();
        System.out.println("线程:"+Thread.currentThread().getName()+"获得读锁");
        Thread.sleep(2000); 
        lock.readLock().unlock();
        System.out.println("线程:"+Thread.currentThread().getName()+"释放读锁");
    }

}
  • 线程类(验证是否可以进入输出语句)
public class MyThreadA extends Thread {
    private Service service;
    public MyThreadA(Service service) {
        this.service=service;
    }
    public void run() {
        try {
            service.printString();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
  • 创建多个线程
public class app {
    public static void main(String[] args) throws InterruptedException {
            Service service=new Service();
            MyThreadA a=new MyThreadA(service);
            MyThreadA b=new MyThreadA(service);
            MyThreadA c=new MyThreadA(service);
            MyThreadA d=new MyThreadA(service);
            a.start();
            b.start();
            c.start();
            d.start();

    }

}
  • 运行结果

Java 接口数据加密传输 java接口加锁_多线程_03

3.1.2注意事项

  • 注意:四个线程可以同时获得锁对象。也就是说读锁之间可以共享数据,不会互相排斥。

3.2 ReentrantReadWriteLock类实现互斥现象

  • 在读取数据的时候,是可以共享的,但是我们如果想要写数据,那么对我们的要求就非常高了,一旦和写数据相关,就必须要实现数据互斥现象,比如写写互斥,读写互斥,写读互斥
  • 首先写一个业务类(实现读锁和写锁的实现)
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service {
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
    public void read() throws Exception
    {
        lock.readLock().lock();
        System.out.println("线程:"+Thread.currentThread().getName()+"获得读锁");
        Thread.sleep(2000); 
        lock.readLock().unlock();
        System.out.println("线程:"+Thread.currentThread().getName()+"释放读锁");
    }
    public void write() throws Exception
    {
        lock.writeLock().lock();
        System.out.println("线程:"+Thread.currentThread().getName()+"获得写锁");
        Thread.sleep(2000); 
        lock.writeLock().unlock();
        System.out.println("线程:"+Thread.currentThread().getName()+"释放写锁");
    }

}
  • 创建写线程
public class MyThreadB extends Thread {
    private Service service;
    public MyThreadB(Service service) {
        this.service=service;
    }
    public void run() {
        try {
            service.write();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
  • 创建读线程
public class MyThreadA extends Thread {
    private Service service;
    public MyThreadA(Service service) {
        this.service=service;
    }
    public void run() {
        try {
            service.read();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

3.2.1 读写互斥现象(写读互斥)

  • main函数
public class app {
    public static void main(String[] args) throws InterruptedException {
            Service service=new Service();
            MyThreadA a=new MyThreadA(service);
            MyThreadB b=new MyThreadB(service);
            a.start();
            b.start();

    }

}
  • 运行结果

Java 接口数据加密传输 java接口加锁_加锁_04

3.2.2 写写互斥现象

  • main函数
public class app {
    public static void main(String[] args) throws InterruptedException {
            Service service=new Service();
            MyThreadB a=new MyThreadB(service);
            MyThreadB b=new MyThreadB(service);
            a.start();
            b.start();

    }

}
  • 运行结果

Java 接口数据加密传输 java接口加锁_加锁_05

4.总结

在本篇博客中主要介绍了在jdk5之后我们可以利用Lock对象,实现多线程之间的同步加锁。如果利用Lock对象加锁,以及如果想要提升线程效率,比如只是线程之间的读操作,我们可以使用ReentrantReadWriteLock对象。