![a4f5d70aaafe1c07e954cc493905b5e4.gif](https://i-blog.csdnimg.cn/blog_migrate/241a1664edecc6e4184d405139799e03.gif)
![0bc574528df0d3192c55b52e46ec584b.png](https://i-blog.csdnimg.cn/blog_migrate/54625f535a7e4ebed6ef11ab9240ac0b.png)
![ddbd52f9a727021a6ddf1874678fa777.png](https://i-blog.csdnimg.cn/blog_migrate/00d15ae8e592f547aaa793e07f7a2025.png)
synchronized可以为任意对象加锁,用法比较灵活,语法如下
(1)修饰代码块,作用于调用的对象;
(2)修饰方法,作用于调用的对象;
(3)修饰静态方法,作用于所有对象;
(4)修饰类,作用于所有对象。
synchronized取得的锁都是对象锁,而不是把一段代码(或者方法)当成锁!
使用synchronized时,应该注意以下细节问题:
0 1synchronized锁的重入性如下列代码所示,类似于ReentrantLock
在method1中没有释放锁的情况下,可以继续调用synchronized修饰的method2
public class SyncDubbo { public synchronized void method1(){ System.out.println("method1..."); method2(); } public synchronized void method2(){ System.out.println("method2..."); method3(); } public synchronized void method3(){ System.out.println("method1..."); } public static void main(String[] args) { final SyncDubbo sd = new SyncDubbo(); Thread t1 = new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub sd.method1(); } }, "t1"); t1.start(); }}
如下列代码所示,在子类与父类之间相互调用也运用了synchronized的重用性
public class FatherSon { static class Father { public int num = 10; public synchronized void method1(){ try { num --; System.out.println("Father num = " + num); Thread.sleep(100); } catch (Exception e) { // TODO: handle exception } } } static class Son extends Father { public synchronized void method2(){ try { while (num >0) { num --; System.out.println("Son num = " + num); Thread.sleep(100); this.method1(); } } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub Son son = new Son(); son.method2(); } }, "t1"); t1.start(); }}
02
不要使用字符串常量作为锁
如下列代码所示,使用了字符串常量作为锁,那么t1和t2运行之后将会一直在t1中出现死循环,t2永远拿不到锁!
解决:可以使用 new String("字符串常量") 作为锁
public class StringLock { public void method(){ synchronized("字符串常量"){ try { while (true){ System.out.println("当前线程:" + Thread.currentThread().getName() + "开始"); Thread.sleep(1000); System.out.println("当前线程:" + Thread.currentThread().getName() + "结束"); } } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { final StringLock sl = new StringLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub sl.method(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub sl.method(); } }, "t2"); t1.start(); t2.start(); }}
03
锁对象的改变问题
如下列代码所示,使用synchronized锁住了一个对象,并且在代码里重新new了一个对象,导致锁对象改变。
这样在t1还没有运行完代码的时候,t2就已经可以拿到锁了,显然会出现问题!
解决:不要改变锁对象(但是改变对象的属性对代码不会有影响,比如锁是一个人,那么可以改变这个人的身高、年龄等)
public class ChangeLock { Object obj = new Object(); public void method(){ synchronized(obj){ try { System.out.println("当前线程:" + Thread.currentThread().getName() + "开始"); //从这里改变锁对象 obj = new Object(); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "结束"); } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { final ChangeLock cl = new ChangeLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub cl.method(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub cl.method(); } }, "t2"); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); }}
● 架构系列——线程实现方式以及生命周期的探索
● 架构系列——并发、并行与多线程关系探索
● 架构系列——单体、分布式、集群与冗余的探索
● Java反射:框架设计的灵魂
● 高并发秒杀系统如何设计与优化
● 要准备多少东西去面试---java中高级面试总结(值得收藏)
● 最近的面试有感(7个方面)
● 常用数据结构及其Java实现
● java中的参数传递(只有值传递没有引用传递)
● 38张史上最全的IT工程师技能图谱(高清收藏)
● PLSQL连接本地oracle或远程oracle数据库,实现随意切换(送福利)
● 通过数据泵expdp、impdp方式备份与还原Oracle数据库--值得收藏
● java常见排序算法--选择排序、冒泡排序、插入排序分析与比较