- 引言
- ReentrantLock类的使用
- 1 使用ReentrantLock进行加锁
- 11 代码实现
- 12 注意事项
- 2 ReentrantLock类实现等待通知机制
- 21 代码实现
- 22注意事项
- ReentrantReadWriteLock的使用
- 1 使用ReentrantReadWriteLock对象实现共享
- 11 代码实现
- 12注意事项
- 2 ReentrantReadWriteLock类实现互斥现象
- 21 读写互斥现象写读互斥
- 22 写写互斥现象
- 总结
1.引言
在前面的博客中,要想实现线程之间的同步,可以通过添加synchronized
关键字来实现,在JDK5以后,出现了一个接口叫做Lock
,也就是说:在JDK5以后,将锁也定义成了对象 ,并且锁对象所实现的功能比synchronized
关键字更加强大。在此篇博客中,主要介绍两个类的使用ReentrantLock
和ReentrantReadWriteLock
。
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();
}
}
- 运行结果
2.2.2注意事项
- 注意和
synchronized
关键字区分,在synchronized
关键字中,如果想要唤醒线程,使用notify()
方法和notifyAll()
方法 - 在
Lock
对象中,想要唤醒线程是使用signal()
方法和signalAll()
方法 - 注意下图,等待通知机制中
synchronized
和lock
的区别,对于lock
对象添加了一个condition
对象
- 注意:
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();
}
}
- 运行结果
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();
}
}
- 运行结果
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();
}
}
- 运行结果
4.总结
在本篇博客中主要介绍了在jdk5之后我们可以利用Lock对象,实现多线程之间的同步加锁。如果利用Lock对象加锁,以及如果想要提升线程效率,比如只是线程之间的读操作,我们可以使用ReentrantReadWriteLock对象。