当前位置: 首页 - 行业资讯 - 同步只会用synchronized如果你没用过它 面试就等著挨虐吧

同步只会用synchronized如果你没用过它 面试就等著挨虐吧

2024-12-19 行业资讯 0

同步只会用synchronized?如果你没用过它 面试就等著挨虐吧

作者 Java圣斗士 原创文章,转载请注明出处

全文2000字,阅读大约需要10分钟,建议收藏

大家好,我是又皮又可爱的Java圣斗士,关注我,每天带你飞!

我:强子,我看你今天气色不好,是哪里不舒服吗?

强子:你也知道的,最近正在准备跳槽,可是面了几家,都被刷了。

我:不要气馁,再接再厉嘛!

强子:哎,都怪我平时心浮气躁,知识掌握不牢固,有些知识是平时不太常用的,但是面试的时候却极容易问到,比如ArrayList、HashMap这些容器的内部实现。还有一些多执行绪的问题。明明订阅了一大堆公众号,一大批优质的头条号,他们每天分享干活,却只是沦为我的收藏不看系列。

我:是啊,现在的年轻人,能够慢下来认真总结一些知识很不容易,那些只收藏却不看的人,对进步一无所知,哈哈。

You know nothing

强子:哎对了,前些天面试的时候,有技术官问我Lock有没有用过。可是我连听都没听过。

我:的确,Lock在日常的业务开发中很少用到,但却是必不可少的并发程式设计基础知识。

强子:那你快说说这个Lock是个什么东东。

我:Lock是手动锁的一个抽象界面是为了操作更加细致的同步场景而提供的。一般在使用的时候,往往会选择它的一个实现类ReentrantLock它可以完成类似synchronized(this)的功能。

public class ReentrantLock implements Lock, java.io.Serializable

强子:居然可以代替synchronized?那这两者有什么区别呢?

我:当然有区别,记住!synchronized线上程执行完毕之后,会自动释放占有的物件锁,而Lock即便是执行绪执行完毕也不会释放锁资源,必须要手动释放锁!必须要手动释放锁!必须要手动释放锁!

强子:吼吼,你说了三遍,看来这是个重点!

我:而且,synchronized在遇到异常之后也会自动释放锁,而 Lock 呢?

强子:必须手动释放锁!?

我:哈,总算记住了。没错,Lock就算遇到异常也不会释放锁,释放锁的唯一途径只有呼叫unlock()方法手动释放。所以,经常要在finally子句中进行Lock的锁的释放。我们来简单写一段程式来看看怎么应用吧。

来看一下下面的程式码:

public class ReentrantLockDemo {

Lock lock = new ReentrantLock();

void m1() {

try {

lock.lock();

for (int i = 0; i TimeUnit.SECONDS.sleep(1);

System.out.print(i + );

}

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();// 比较一下未释放锁前后的执行区别

}

}

void m2() {

lock.lock();

System.out.println(m2...);

lock.unlock();

}

public static void main(String[] args) {

ReentrantLockDemo r1 = new ReentrantLockDemo();

new Thread(r1::m1).start();

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

new Thread(r1::m2).start();

}

}

输出结果:

0 1 2 3 4 5 6 7 8 9 m2...

main中m1和m2间隔1秒启动,但是在m1占有lock的时候,m2必须等待,直到m1执行unlock()方法,释放锁资源,m2才可以执行。

强子:哦,这个例子还蛮简单的。

我:前面也提到了,Lock是一种操作更为细致的同步机制,非常考验开发者对锁概念的理解以及同步过程的分析。有时候,执行绪可能并非一定要获得锁资源不可,它可能会尝试进行锁的请求,如果请求不到,那么根据业务的需要,要么继续执行,要么设定等待时间,如果时间到了还是没有获得锁,那么将会继续执行剩余的任务。比如下面的程式码:

我们将m2方法进行一些改造,来看看执行效果:

void m2() {

boolean locked = false;

try {

locked = lock.tryLock(3, TimeUnit.SECONDS);

System.out.print(m2 resume... );

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

if (locked)

lock.unlock();

}

}

tryLock()方法支援有参无参两种过载,正如上面所说,如果未指定时间,那么在获取不到锁的情况下会立刻执行剩下的逻辑,那么如果设定了时间,像上面的程式码中一样,等待3秒后,继续执行。执行结果如下:

0 1 2 3 m2 resume... 4 5 6 7 8 9

可以看到,m2晚于m1一秒之后执行,然后立刻请求锁资源,但是这个时候m1 的锁还没有释放,那么m2会进行tryLock,3秒,但是依然没有拿到锁资源,所以就会继续执行m2 的剩余逻辑,输出 “m2 resume...”。

强子:厉害了,还挺智慧的!

我:来来来,再教你一个通过ReentrantLock实现公平锁的方法。

强子:公平锁?这个怎么讲?

我:我们知道,在多执行绪共同竞争锁资源的时候,往往预设都是抢占式的,也就是说,运气好,谁抢到谁就执行,其他执行绪等待。公平锁的意思就是,通过某种机制,来实现所有的竞争执行绪都能够均匀使用锁资源。典型的非公平锁就是synchronized公平锁会让等待时间最长的执行绪优先执行。来看下面的小例子:

public class FairLockDemo extends Thread {

/** 引数为true为公平锁 */

private static Lock lock = new ReentrantLock(true);

@Override

public void run() {

for (int i = 0; i try {

lock.lock();

System.out.println(Thread.currentThread().getName() + 获得锁);

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) {

FairLockDemo demo = new FairLockDemo();

Thread th1 = new Thread(demo);

Thread th2 = new Thread(demo);

th1.start();

th2.start();

}

}

执行结果:

输出比较长,我就不全部展示了,从一开始的输出可以看出,执行绪1 和执行绪2 获得锁的概率是一样大的,这就是我刚才说的公平锁。而你只需要简简单单、轻描淡写地在ReentrantLock()的构造器中加入一个true,即可实现公平锁!就是这么Easy!

强子:天哪,这个Lock还真是强大,不过用法也比synchronized复杂的多,我得赶紧回去总结总结。

我:的确,Lock是更加细致的锁的操作,只有手动呼叫unlock()方法才能够释放锁资源。有时候根据业务需要还会尝试tryLock,等等应对各种实际场景。它之所以在面试中经常会被问到不是没有道理的。

---欢迎关注【Java圣斗士】,我是你们的小可爱(✪ω✪) Morty---

---专注IT职场经验、IT技术分享的灵魂写手---

---每天带你领略IT的魅力---

---期待与您陪伴!---

标签: 科技行业资讯

上一篇:高通带来神助攻 魅族新机搭载骁龙处理器稳了

下一篇:中关村手机参数比较孩子作业好帮手联想小新M7268W小体积输出更给力吗

相关推荐
推荐资讯
热门文章