核心内容摘要
如何用Alpakka构建响应式数据流?5分钟快速上手教程
文章目录Java面试必懂深入解析synchronized关键字的用法 ?
什么是synchronized
synchronized的使用方式
synchronized修饰实例方法
synchronized修饰静态方法
synchronized修饰代码块
synchronized的关键点
synchronized的优缺点优点缺点
面试
常见问题
synchronized和ReentrantLock有什么区别
synchronized为什么会引起死锁
如何避免死锁
synchronized和volatile有什么区别
实战案例
六、
总结 领取 | 1000 套高质量面试题大合集无套路闫工带你飞一把Java面试必懂深入解析synchronized关键字的用法 ?大家好我是闫工今天呢咱们来聊一个Java面试中绝对绕不开的话题——synchronized关键字。
作为一个在Java世界里摸爬滚打多年的“老码农”我得告诉你们这个知识点可是线程安全领域的“老祖宗”了。
不管是初学还是进阶掌握它都是你通向高薪offer的必经之路所以咱们今天就从基础到高级再到面试中可能会被问到的各种细节来一场彻底的解析
什么是synchronized简单来说synchronized关键字是用来保证线程安全的。
在线程并发的情况下多个线程同时操作共享资源时容易出现“脏数据”或者“竞态条件”这时候就需要用sync来加把锁让代码块或方法变成“临界区”每次只能有一个线程进入执行。
听起来有点抽象没关系咱们先来看一个经典的例子银行转账问题。
假设A和B两个人同时给C转账100元那么如果不做任何处理可能会出现这样的情况publicclassBank{privateintbalance0;publicvoiddeposit(intamount){balanceamount;// 这里可能有竞态条件}publicstaticvoidmain(String[]args){BankbanknewBank();Threadt1newThread(()-bank.deposit(
);Threadt2newThread(()-bank.deposit(
);t
start();t
start();}}上面的代码中deposit方法没有加锁所以A和B可能同时读取到balance0然后都执行balance 100结果就是balance变成100而不是200。
这就是线程不安全的表现。
那怎么解决呢很简单给deposit加上synchronizedpublicsynchronizedvoiddeposit(intamount){balanceamount;}这样每次只有一个线程能执行这个方法问题就解决了
synchronized的使用方式
synchronized修饰实例方法这是最常见的用法。
当一个实例方法被synchronized修饰时锁的对象是调用该方法的那个具体实例也就是this。
例如publicclassCounter{privateintcount0;publicsynchronizedvoidincrement(){count;}}这样在同一时间只有一个线程可以执行increment()方法。
synchronized修饰静态方法有时候我们需要对静态方法加锁。
比如一个类级别的计数器publicclassStaticCounter{privatestaticintcount0;publicsynchronizedstaticvoidincrement(){count;}}这时候锁的对象是这个类的Class对象StaticCounter.class所以不管有多少个实例静态方法只有一个共享的锁。
synchronized修饰代码块有时候我们不需要对整个方法加锁只需要对一部分代码加锁。
这时可以用synchronized包裹一个代码块publicclassThreadSafeBank{privateintbalance;publicvoidtransfer(intfromAccountId,inttoAccountId,intamount){synchronized(fromAccountId){// 锁住fromAccountId// 执行转账逻辑...}synchronized(toAccountId){// 锁住toAccountId// ...}}}这样可以更灵活地控制锁的范围。
synchronized的关键点锁的对象无论是方法还是代码块synchronized都是基于对象来加锁的。
实例方法用this静态方法用Class对象代码块可以用任意对象。
可重入性同一个线程可以多次获取同一个锁而不会死锁。
比如一个线程调用了synchronized方法A然后在方法A里又调用了另一个synchronized方法B这时候JVM会知道是同一线程允许进入。
synchronized的优缺点优点简单直接使用起来非常直观几乎不需要额外配置。
内置支持JDK原生支持不需要引入第三方库。
缺点性能问题在高并发场景下synchronized的粒度可能太大导致线程 contention争用影响性能。
灵活性不足只能提供粗粒度的锁控制无法像ReentrantLock那样支持公平锁、可中断锁等高级特性。
面试
常见问题
synchronized和ReentrantLock有什么区别语法层面synchronized是关键字语法糖而ReentrantLock是一个类需要显式调用方法。
功能层面ReentrantLock提供了更多的灵活性比如支持公平锁、可中断锁等但使用起来也更复杂。
synchronized为什么会引起死锁当两个线程互相等待对方释放锁时就会发生死锁。
例如publicclassDeadlockExample{publicstaticvoidmain(String[]args){Threadt1newThread(()-{synchronized(String.class){// 锁Atry{Thread.sleep(
;}catch(InterruptedExceptione){}synchronized(Integer.class){// 锁BSystem.out.println(Thread 1 done);}}});Threadt2newThread(()-{synchronized(Integer.class){// 锁Btry{Thread.sleep(
;}catch(InterruptedExceptione){}synchronized(String.class){// 锁ASystem.out.println(Thread 2 done);}}});t
start();t
start();}}如果t1先拿到锁A而t2先拿到锁B那么两个线程都会卡在等待对方释放锁的状态导致死锁。
如何避免死锁按顺序加锁所有线程都按照固定的顺序获取锁。
使用tryLock()方法如果无法立即获得锁则不阻塞当前线程而是返回一个布尔值表示是否成功。
synchronized和volatile有什么区别作用synchronized是关于多线程的互斥与同步而volatile是关于内存可见性。
粒度synchronized可以控制一段代码块或方法而volatile只能修饰变量。
实战案例假设我们有一个共享资源池需要确保每次只有一个线程能获取资源。
这时候可以用synchronized来实现publicclassResourcePool{privateObjectresource;publicsynchronizedvoidgetResource(){if(resourcenull){// 模拟资源加载try{Thread.sleep(
;}catch(InterruptedExceptione){}resourcenewObject();System.out.println(Resource created by Thread.currentThread().getName());}}publicstaticvoidmain(String[]args){ResourcePoolpoolnewResourcePool();Runnabletask()-pool.getResource();for(inti0;i10;i){newThread(task,Thread-i).start();}}}这样所有线程都会排队等待获取锁从而保证资源只被创建一次。
六、