最后
这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档
祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!
感谢大家的支持!!
public class Demo {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
//创建俩个线程
Thread a = new Thread(“A”) {
@Override
public void run() {
service.methodA();
}
};
Thread b = new Thread(“B”) {
@Override
public void run() {
service.methodA();
}
};
a.start();
b.start();
a.join();
b.join();
}
}
- 运行结果
- 从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印的数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。
ReentrantLock的公平锁与非公平锁
- 公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一一样的就是先来的不- -定先得到锁,这个方式可能造成某些线程一直拿不到锁, 结果也就是不公平的了。
代码演示
- 服务类
public class Service2 {
private ReentrantLock reentrantLock;
public Service2(boolean isFire) {
this.reentrantLock = new ReentrantLock(isFire);
}
public void method() {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + " 获取锁");
reentrantLock.unlock();
}
}
- 运行类
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
//传入true表示公平, 传入false表示不公平
Service2 service2 = new Service2(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
service2.method();
}
};
//创建多个线程
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
for (int i = 0; i < 10; i++) {
threads[i].join();
}
}
}
- 当我们传入的是true表示公平锁 其运行结果为
- 当我们传入false表示不公平时运行结果为
- 从结果上就可以看出差距, 公平锁上锁的顺序是有序的, 还要注意ReentrantLock默认的是非公平锁
**
-
通过看源码可以发现, 如果是公平锁, 在lock的时候会走fairSync,
-
然后判断此时队列中有没有等待的线程, 如果有就加入队列尾部保证公平性
-
if (!tryAcquire(arg) &&
-
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
-
selfInterrupt();
-
}
-
如果不是公平的, 就会走NoFairSync, 直接CAS检查此时的state是不是为0, 如果此时正好为0就去执行了
-
也就破坏了公平性
-
final void lock() {
-
if (compareAndSetState(0, 1))
-
setExclusiveOwnerThread(Thread.currentThread());
-
else
-
acquire(1);
-
}
-
关键字synchronized与wait()和notifyO/notifyAllO)方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition 对象。Condition 类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。在使用notify()/notifyAllO)方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。
-
而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll)时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。
Condition的实现分析
-
首先Condition的操作需要在获取到相关锁的条件下, 所以他是作为同步器的内部类的
-
每个Condition对象内部都有一个队列, 是一个等待队列, 该队列是Condition对象实现等待 通知的功能关键
-
这个等待队列是一个FIFO先进先出的队列, 在队列的每一个节点上都是一个线程的引用, 该线程就是Condition对象上等待的线程, 如果一个线程执行了Condition.await() 就会释放锁然后进入这个队列中, 等待通知的时候, 也就是执行Condition.signal() 方法时, 将会唤醒这个Condition对象的等待队列中的等待时间最长的那个线程, 也就是头结点引用的那个线程, 这个线程就会进入到同步队列中, 并将这个线程再等待队列中删掉
-
使用Condition和synchronized一样都要在加锁的情况下使用, 否则会报异常
演示Condition的等待与通知
-
Object类中的wait()方法相当于Condition类中的await()方法。
-
Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。
-
Object类中的notify方法相当于Condition类中的signal(方法。
-
Object类中的notifyAll 方法相当于Condition类中的signalAll()方法。
-
下面进行演示
-
服务类
public class Service3 {
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition condition = reentrantLock.newCondition();
public void methodAwait() {
System.out.println(Thread.currentThread().getName() + " 开始运行");
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + " 执行await");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.println(Thread.currentThread().getName() + " 运行结束");
}
}
public void methodSignal() {
System.out.println(Thread.currentThread().getName() + " 开始运行");
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + " 执行signal");
try {
condition.signal();
} finally {
reentrantLock.unlock();
System.out.println(Thread.currentThread().getName() + " 运行结束");
}
}
}
- 运行类
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
Service3 service3 = new Service3();
Thread a = new Thread(“A”) {
@Override
public void run() {
service3.methodAwait();
}
};
Thread b = new Thread(“B”) {
@Override
public void run() {
service3.methodSignal();
}
};
a.start();
Thread.sleep(100);
b.start();
}
}
- 运行结果
实现部分路通知和选择路通知
- 服务类
public class Service4 {
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition conditionA = reentrantLock.newCondition();
private Condition conditionB = reentrantLock.newCondition();
public void awaitA() {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + " 开始运行");
System.out.println(Thread.currentThread().getName() + " 执行awaitA");
try {
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
最常见Java面试题解析(2021最新版)
2021企业Java面试题精选
4)]
[外链图片转存中…(img-9eIuUn4s-1715291203795)]
最常见Java面试题解析(2021最新版)
[外链图片转存中…(img-ZABtZsOp-1715291203795)]
[外链图片转存中…(img-a514JpOF-1715291203795)]
2021企业Java面试题精选
[外链图片转存中…(img-czyRJTrT-1715291203796)]
[外链图片转存中…(img-I4hzhrdy-1715291203796)]