更新时间:2020-11-24 17:33:46 来源:极悦 浏览1098次
为了解决竞争条件带来的问题,我们可以对资源上锁。多个线程共同读写的资源称为共享资源,也叫临界资源。涉及操作临界资源的代码区域称为临界区(Critical Section)。同一时刻,只能有一个线程进入临界区。我们把这种情况称为互斥,即不允许多个线程同时对共享资源进行操作,在同一时间只能被一个线程所占有的锁称之为Java多线程互斥锁。
互斥锁在java中的实现就是 ReetranLock , 在访问一个同步资源时,它的对象需要通过方法 tryLock() 获得这个锁,如果失败,返回 false,成功返回true。根据返回的信息来判断是否要访问这个被同步的资源。ReentrantLock 互斥锁是可重入锁,即某一线程可多次获得该锁。
进入临界区前,需要先获得互斥锁。如果已经有线程正在使用资源,那么需要一直等待,直到其它线程归还互斥锁。
操作完共享资源之后,即退出临界区时,需要归还互斥锁,以便其它等待使用该资源的线程能够进入临界区。
伪代码示例:
wait(lock); //获得互斥锁
{
临界区,操作共享资源
}
signal(lock); //归还互斥锁
Java 中可以使用 ReentrantLock 对临界区上锁,防止多个线程同时进入临界区:
private static Lock bufferLock = new ReentrantLock();
public static void print(String msg) {
bufferLock.lock();
//临界区,操作临界资源 globalBuffer
bufferLock.unlock();
}
这里我们只需要在临界区前使用 lock() 上锁,在临界区后使用 unlock() 解锁即可,java.util.concurrent 帮我们实现了临界区前判断锁状态的工作,会自己决定是阻塞还是进入临界区。
synchronized 关键字
java 为我们提供了更加简便的方式,用于实现临界区的互斥。
例如,我们可以为操作共享资源的函数加上 synchronized 关键字:
public synchronized void myFunction() {
//操作共享资源 A
}
通过这种方式,能够确保同一时刻最多只有一个线程在执行该函数。如果资源 A 只在该函数中读写,那么可以保证资源 A 不会出现被多个线程同时读写的情况。
但是,如果在其它函数中也对共享资源 A 进行操作,那么就不能使用这种方式来实现资源的使用互斥。因为即使这些函数都声明为 synchronized,也只是说明同一时刻不能有多个线程执行同一个函数,但允许多个线程同时执行不同的函数,而这些函数都在操作同一个资源 A。
下面我们给出另一种方法来实现资源使用的互斥锁。
synchronized 代码块
通过声明函数为 synchronized 的方式,只能实现函数体的互斥。要确保资源使用的互斥,即同一时刻只能有一个线程使用该资源,可以将操作资源 A 的语句放入 synchronized 代码块:
public void function1() {
......
synchronized (A) {
//操作资源 A
}
......
}
public void function2() {
......
synchronized (A) {
//操作资源 A
}
......
}
这样,对于资源 A 来说,同一时刻,只能有一个对应的 synchronized 代码块执行。因此,无论是在哪个地方使用资源 A,都不会出现多个线程竞争该资源的情况。
由ReentrantLock 的构造函数可见,在实例化 ReentrantLock 的时候我们可以选择实例化一个公平锁或非公平锁,而默认会构造一个非公平锁。公平锁与非公平锁区别在于竞争锁时的有序与否。Java多线程互斥锁ReentrantLock是通过继承接口Lock而实现的,类似的还有继承 ReadWriteLock 实现的 ReentrantReadWriteLock(读写锁),对此,在本站的Java多线程教程中有进一步的讲解。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习