Synchronized
程序员文章站
2022-07-12 20:35:34
...
Synchronized
一、锁重入
1.概念
关键字 synchronized 拥有锁重入功能,也就是在使用 synchronized 时,当一个线程得到了一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。
2.示例
三个方法,依次调用,获取 第一个方法的锁,执行第二个方法时同样可以获取锁
package com.study.current.thread.day01; /** * 锁重入的机制 * 在获取 m1 的锁后获取 m2 的锁 */ public class SynchronizedDubo1 extends Thread { 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("method3"); } /** * @param args */ public static void main(String[] args) { final SynchronizedDubo1 thread = new SynchronizedDubo1(); Thread t = new Thread(new Runnable() { public void run() { thread.method1(); } }); t.start(); } }
二、父子类
父子类间继承时,使用synchronized 保证线程安全
package com.study.current.thread.day01; /** * 父子关系调用 */ public class SynchronizedDubo2 extends Thread { static class Main{ public int i = 10 ; public synchronized void operationSup(){ i -- ; System.out.println("Main i : "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } static class SubMain extends Main{ public synchronized void operationSub(){ while(i> 0){ i -- ; System.out.println("SubMain i : "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.operationSup(); } } } /** * @param args */ public static void main(String[] args) { Thread thread = new Thread(new Runnable() { public void run() { SubMain syn = new SubMain(); syn.operationSub(); } }); thread.start(); } }
三、异常
当发生异常时,或中断运行(异常数据对后续功能有影响),或继续运行但保存错误数据的日志
package com.study.current.thread.day01; public class SynchronizedException extends Thread { public int count = 0 ; /** * 注意此处的捕获异常,级别为 Exception 即可以捕获此时的两种 Exception * 执行结果:出现异常,但程序依然在运行 */ public synchronized void operation(){ while(true){ count ++ ; try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" count : "+count); if(count == 10){ System.out.println(Integer.valueOf("a")); } } catch (Exception e) { e.printStackTrace(); System.out.println("log log 10 exception"); } } } /** * @param args */ public static void main(String[] args) { final SynchronizedException exc = new SynchronizedException(); Thread t = new Thread(new Runnable() { public void run() { exc.operation(); } },"t1"); t.start(); } }
四、锁种类
使用 synchronized 声明的方法在某些情况下是由弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下可以使用 synchronized 代码块去优化代码执行时间,即减小锁的粒度
锁:
当前对象作为锁
类作为锁
任意对象作为锁
package com.study.current.thread.day01; /** * 锁对象 * 1.当前对象锁 * 2.类锁 * 3.任意对象锁 */ public class ObjectLock extends Thread { public void method1(){ synchronized (this) { // 对象锁,this 指代当前对象 System.out.println("method1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void method2(){ synchronized (ObjectLock.class) { System.out.println("method2"); // 类锁 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } private Object ojt = new Object(); public void method3(){ synchronized (ojt) { // 任意对象锁 System.out.println("method3"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @param args */ public static void main(String[] args) { final ObjectLock lock = new ObjectLock(); Thread t1 = new Thread(new Runnable() { public void run() { lock.method1(); } }); Thread t2 = new Thread(new Runnable() { public void run() { lock.method2(); } }); Thread t3 = new Thread(new Runnable() { public void run() { lock.method3(); } }); t1.start(); t2.start(); t3.start(); } }
五、String 常量不能作为锁
String 常量作为锁,会造成死循环,锁失效
package com.study.current.thread.day01; /** * String 常量作为锁,会出现死循环 */ public class StringLock { public void method(){ /** * 使用字符串常量当做锁,会出现死循环,即当前的运行结果总是 t1 的线程 * 字符串常量只有一个引用 * 解决: * 可以替换为 new String("") */ synchronized ("字符串常量") { System.out.println("method"); try { while(true){ System.out.println(Thread.currentThread().getName()+" start"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" end"); } } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @param args */ public static void main(String[] args) { final StringLock stringLock = new StringLock(); Thread t1 = new Thread(new Runnable() { public void run() { stringLock.method(); } },"t1"); Thread t2 = new Thread(new Runnable() { public void run() { stringLock.method(); } },"t2"); t1.start(); t2.start(); } }
六、不要试图改变锁
改变当前运行的锁,会造成锁的失效
package com.study.current.thread.day01; /** * 不要修改锁对象 * 会导致锁失效 * 开始几个线程争夺 a 锁, 第一个线程 获取 a ,并把 锁改为 b ,第二个线程则无需等待 a 锁的释放,直接获取 b 锁, * 就不能起到锁的作用 */ public class ChangeLock { private String lock = "abc"; public void changeLock(){ synchronized (lock) { System.out.println(Thread.currentThread().getName() + " start"); try { lock = "bcd" ; Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" end"); } } /** * @param args */ public static void main(String[] args) { final ChangeLock lock = new ChangeLock(); Thread t1 = new Thread(new Runnable() { public void run() { lock.changeLock(); } },"t1"); Thread t2 = new Thread(new Runnable() { public void run() { lock.changeLock(); } },"t2"); t1.start(); t2.start(); } }
七、对象锁,其属性值得改变不影响锁的使用
锁对象的改变问题,当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那么持有的锁就不同。
如果对象本身不发生改变,那么依然是同步的,即使是对象的属性发生了改变。
package com.study.current.thread.day01; /** * 对象的属性值的变化,不影响锁的使用 */ public class ModifyLock { private String name ; private String pass ; public synchronized void changeAttribute(String name,String pass){ System.out.println(Thread.currentThread().getName() + " start"); this.setName(name); this.setPass(pass); System.out.println(Thread.currentThread().getName() + " modify name:"+name+" pass:"+pass); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " end"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } /** * @param args */ public static void main(String[] args) { final ModifyLock lock = new ModifyLock(); Thread t1 = new Thread(new Runnable() { public void run() { lock.changeAttribute("zhangsan", "1111"); } },"t1"); Thread t2 = new Thread(new Runnable() { public void run() { lock.changeAttribute("lisi", "2222"); } },"t2"); t1.start(); t2.start(); } }
上一篇: Java内存模型JMM之六深入理解synchronized(1)
下一篇: 脏读(数据)
推荐阅读
-
一文让你读懂Synchronized底层实现,秒杀面试官
-
Java多线程-同步:synchronized 和线程通信:生产者消费者模式
-
详细解读java同步之synchronized解析
-
解决Maven 项目报错 java.httpservlet和synchronized使用方法
-
死磕Synchronized底层实现,面试你还怕什么?
-
【Java并发编程】24、Synchronized实现原理解析
-
售票情景解读synchronized和Lock两种锁的区别
-
java高并发系列 - 第10天:线程安全和synchronized关键字
-
java并发笔记四之synchronized 锁的膨胀过程(锁的升级过程)深入剖析
-
java多线程关键字volatile、lock、synchronized