是Java内建的同步机制,所以也有人称其为Intrinsic Locking,它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他试图获取的线程只能等待或者阻塞在那里。
通常翻译为再入锁,是Java 5提供的锁实现,它的语义和synchronized基本相同。再入锁通过代码直接调用lock()方法获取,代码书写也更加灵活。与此同
时, ReentrantLock提供了很多实用的方法,能够实现很多synchronized无法做到的细节控制,比如可以控制fairness,也就是公平性,或者利用定义条件等。但是,编码中也需要注意,必须要明确调用unlock()方法释放,不然就会一直持有该锁。
早期版本synchronized在很多场景下性能相差较大,在后续版本进行了较多改进,在低竞争场景中表现可能优于ReentrantLock。
类在被多个线程访问的时候,类可以持续进行正确的行为。
当多个线程访问一个类的时候,如果不考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步以及在调用方法代码不必作为其他的协调,这个类仍然是正确的,那么称这个类是安全的。
Java中的每一个对象都可以作为锁。具体表现
为以下3种形式:
以上来自《java多线程编程核心技术》
我们根本无法进行公平性的选择
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod(){
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() +" "+(i+ 1));
}
lock.unlock();
}
}
public class MyThread extends Thread {
private MyService service;
public MyThread(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.testMethod();
}
}
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
MyThread a1 = new MyThread(myService);
MyThread a2 = new MyThread(myService);
MyThread a3 = new MyThread(myService);
MyThread a4 = new MyThread(myService);
MyThread a5 = new MyThread(myService);
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
}
结果
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-0 1
ThreadName = Thread-0 2
ThreadName = Thread-0 3
ThreadName = Thread-0 4
ThreadName = Thread-0 5
ThreadName = Thread-4 1
ThreadName = Thread-4 2
ThreadName = Thread-4 3
ThreadName = Thread-4 4
ThreadName = Thread-4 5
ThreadName = Thread-3 1
ThreadName = Thread-3 2
ThreadName = Thread-3 3
ThreadName = Thread-3 4
ThreadName = Thread-3 5
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try {
lock.lock();
System.out.println("awaits时间为" + System.currentTimeMillis());
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("await锁被释放了!");
}
}
public void signal(){
try {
lock.lock();
System.out.println("signal时间为" + System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
System.out.println("signal锁被释放了!");
}
}
}
public class MyThread extends Thread {
private MyService service;
public MyThread(MyService service) {
this.service = service;
}
@Override
public void run() {
super.run();
service.await();
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
MyThread a1 = new MyThread(myService);
a1.start();
Thread.sleep(3000);
myService.signal();
}
}
结果:
awaits时间为1559575104934
signal时间为1559575107949
signal锁被释放了!
await锁被释放了!
结论:
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
对不同方法调用不同对象。
https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem
它是表示当一个线程试图获取一个它已经获取的锁时,这个获取动作就自动成功,这是对锁获取粒度的一个概念,也就是锁的持
有是以线程为单位而不是基于调用次数。
Java锁实现强调再入性是为了和pthread的行为进行区分。
,我们可在创建再入锁时选择是否是公平的。
ReentrantLock fairLock = new ReentrantLock(true);
这里所谓的公平性是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时间最久的线程。公平性是减少线程“饥饿”(个别线程长期等待锁,但始终无法获取)情况发生的一个办法。
如果使用synchronized,我们根本无法进行公平性的选择,其永远是不公平的,这也是主流操作系统线程调度的选择。通用场景中,公平性未必有想象中的那么重要, Java默认的调度策略很少会导致 “饥饿”发生。与此同时,若要保证公平性则会引入额外开销,自然会导致一定的吞吐量下降。所以,我建议只有当你的程序确实有公平性需要的时候,才有必要指定它。
tryLock(long timeout, TimeUnit unit)
lockInterruptibly()
public class DealThread implements Runnable{
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
public void setFlag(String username){
this.username = username;
}
@Override
public void run() {
if(username.equals("a")){
synchronized (lock1){
try {
System.out.println("username =" + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2){
System.out.println("按 lock1 -> lock2代码执行了");
}
}
}
if(username.equals("b")){
synchronized (lock2){
try {
System.out.println("username =" + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1){
System.out.println("按 lock2 -> lock1代码执行了");
}
}
}
}
}
run方法
public class Run {
public static void main(String[] args) {
try {
DealThread t1 = new DealThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(100);
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.进入jdk安装bin目录
2.运行jps命令 得到运行的Run id值
3.执行jstack命令
jstack -l 9900
参考:
极客时间:《Java核心技术面试精讲》
本笔记根据专栏主题进行学习笔记,虽然参考了许多做了笔记,但是加上了自己的整理,跟原作者的行文可能有很大偏差。如果想查看原版请自行搜索。谢谢