每天一例多线程[day6]-----synchronized细节

syhchronized写法举例

 
  1. package com.jeff.base.sync006;

  2.  
  3. /**

  4. * 使用synchronized声明的方法在某些情况下是有弊端的,

  5. * 比如:A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的事件才能执行,

  6. * 这样的情况下可以使用synchronized去优化代码的执行时间,也就是通常所说的减小锁的粒度。

  7. *

  8. * synchronized可以使用任意的Object进行加锁,用法比较灵活。

  9. *

  10. * 使用synchronized代码块加锁,比较灵活

  11. * @author jeff

  12. *

  13. */

  14. public class ObjectLock {

  15.  
  16. public void method1(){

  17. synchronized (this) { //对象锁

  18. try {

  19. System.out.println("do method1..");

  20. Thread.sleep(2000);

  21. } catch (InterruptedException e) {

  22. e.printStackTrace();

  23. }

  24. }

  25. }

  26.  
  27. public void method2(){ //类锁

  28. synchronized (ObjectLock.class) {

  29. try {

  30. System.out.println("do method2..");

  31. Thread.sleep(2000);

  32. } catch (InterruptedException e) {

  33. e.printStackTrace();

  34. }

  35. }

  36. }

  37.  
  38. private Object lock = new Object();

  39. public void method3(){ //任何对象锁

  40. synchronized (lock) {

  41. try {

  42. System.out.println("do method3..");

  43. Thread.sleep(2000);

  44. } catch (InterruptedException e) {

  45. e.printStackTrace();

  46. }

  47. }

  48. }

  49.  
  50.  
  51. public static void main(String[] args) {

  52.  
  53. final ObjectLock objLock = new ObjectLock();

  54. Thread t1 = new Thread(new Runnable() {

  55. @Override

  56. public void run() {

  57. objLock.method1();

  58. }

  59. });

  60. Thread t2 = new Thread(new Runnable() {

  61. @Override

  62. public void run() {

  63. objLock.method2();

  64. }

  65. });

  66. Thread t3 = new Thread(new Runnable() {

  67. @Override

  68. public void run() {

  69. objLock.method3();

  70. }

  71. });

  72.  
  73. t1.start();

  74. t2.start();

  75. t3.start();

  76.  
  77.  
  78. }

  79.  
  80. }

String常量锁

 
  1. package com.jeff.base.sync006;

  2. /**

  3. * synchronized代码块对字符串的锁,注意String常量池的缓存功能

  4. * @author jeff

  5. *

  6. */

  7. public class StringLock {

  8.  
  9. public void method() {

  10. //new String("字符串常量")

  11. synchronized ("字符串常量") {

  12. try {

  13. while(true){

  14. System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");

  15. Thread.sleep(1000);

  16. System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");

  17. }

  18. } catch (InterruptedException e) {

  19. e.printStackTrace();

  20. }

  21. }

  22. }

  23.  
  24. public static void main(String[] args) {

  25. final StringLock stringLock = new StringLock();

  26. Thread t1 = new Thread(new Runnable() {

  27. @Override

  28. public void run() {

  29. stringLock.method();

  30. }

  31. },"t1");

  32. Thread t2 = new Thread(new Runnable() {

  33. @Override

  34. public void run() {

  35. stringLock.method();

  36. }

  37. },"t2");

  38.  
  39. t1.start();

  40. t2.start();

  41. }

  42. }

打印:

 
  1. 当前线程 : t1开始

  2. 当前线程 : t1结束

  3. 当前线程 : t1开始

  4. 当前线程 : t1结束

  5. .......................

一直就执行了t1的线程,原因是对于字符串常量,在常量池中引用是不变的,所以线程会永远持有String常量锁而不释放。

如果想让t2执行且使用String锁,可以把

new String("字符串常量")

这样就可以让t2和t1同时执行,是两把独立的锁。

 

改变常量锁

 
  1. package com.jeff.base.sync006;

  2. /**

  3. * 锁对象的改变问题

  4. * @author jeff

  5. *

  6. */

  7. public class ChangeLock {

  8.  
  9. private String lock = "lock";

  10.  
  11. private void method(){

  12. synchronized (lock) {

  13. try {

  14. System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");

  15. lock = "change lock";

  16. Thread.sleep(2000);

  17. System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");

  18. } catch (InterruptedException e) {

  19. e.printStackTrace();

  20. }

  21. }

  22. }

  23.  
  24. public static void main(String[] args) {

  25.  
  26. final ChangeLock changeLock = new ChangeLock();

  27. Thread t1 = new Thread(new Runnable() {

  28. @Override

  29. public void run() {

  30. changeLock.method();

  31. }

  32. },"t1");

  33. Thread t2 = new Thread(new Runnable() {

  34. @Override

  35. public void run() {

  36. changeLock.method();

  37. }

  38. },"t2");

  39. t1.start();

  40. try {

  41. Thread.sleep(100);

  42. } catch (InterruptedException e) {

  43. e.printStackTrace();

  44. }

  45. t2.start();

  46. }

  47.  
  48. }

在t1线程执行的过程中,t1所持有的锁常量“lock“,被改变,导致锁被突然释放,t2线程获得了锁。

 

lock = "change lock";

打印:

 
  1. 当前线程 : t1开始

  2. 当前线程 : t2开始

  3. 当前线程 : t1结束

  4. 当前线程 : t2结束

那么,对象的属性被修改,对象锁会不会被释放?

改变对象锁属性

 
  1. package com.jeff.base.sync006;

  2. /**

  3. * 同一对象属性的修改不会影响锁的情况

  4. * @author jeff

  5. *

  6. */

  7. public class ModifyLock {

  8.  
  9. private String name ;

  10. private int age ;

  11.  
  12. public String getName() {

  13. return name;

  14. }

  15. public void setName(String name) {

  16. this.name = name;

  17. }

  18. public int getAge() {

  19. return age;

  20. }

  21. public void setAge(int age) {

  22. this.age = age;

  23. }

  24.  
  25. public synchronized void changeAttributte(String name, int age) {

  26. try {

  27. System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始");

  28. this.setName(name);

  29. this.setAge(age);

  30.  
  31. System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: "

  32. + this.getName() + ", " + this.getAge());

  33.  
  34. Thread.sleep(2000);

  35. System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束");

  36. } catch (InterruptedException e) {

  37. e.printStackTrace();

  38. }

  39. }

  40.  
  41. public static void main(String[] args) {

  42. final ModifyLock modifyLock = new ModifyLock();

  43. Thread t1 = new Thread(new Runnable() {

  44. @Override

  45. public void run() {

  46. modifyLock.changeAttributte("张三", 20);

  47. }

  48. },"t1");

  49. Thread t2 = new Thread(new Runnable() {

  50. @Override

  51. public void run() {

  52. modifyLock.changeAttributte("李四", 21);

  53. }

  54. },"t2");

  55.  
  56. t1.start();

  57. try {

  58. Thread.sleep(100);

  59. } catch (InterruptedException e) {

  60. e.printStackTrace();

  61. }

  62. t2.start();

  63. }

  64.  
  65. }

打印:

 
  1. 当前线程 : t1 开始

  2. 当前线程 : t1 修改对象内容为: 张三, 20

  3. 当前线程 : t1 结束

  4.  
 
  1. 当前线程 : t2 开始

  2. 当前线程 : t2 修改对象内容为: 李四, 21

  3. 当前线程 : t2 结束

t1执行完,t2才开始执行,说明对象属性的改变不会影响线程对锁的持有

 

乐观锁(代码块粒度)

 
  1. package com.jeff.base.sync006;

  2.  
  3. /**

  4. * 使用synchronized代码块减小锁的粒度,提高性能

  5. * @author jeff

  6. *

  7. */

  8. public class Optimize {

  9.  
  10. public void doLongTimeTask(){

  11. try {

  12.  
  13. System.out.println("当前线程开始:" + Thread.currentThread().getName() +

  14. ", 正在执行一个较长时间的业务操作,其内容不需要同步");

  15. Thread.sleep(2000);

  16.  
  17. synchronized(this){

  18. System.out.println("当前线程:" + Thread.currentThread().getName() +

  19. ", 执行同步代码块,对其同步变量进行操作");

  20. Thread.sleep(1000);

  21. }

  22. System.out.println("当前线程结束:" + Thread.currentThread().getName() +

  23. ", 执行完毕");

  24.  
  25. } catch (InterruptedException e) {

  26. e.printStackTrace();

  27. }

  28. }

  29.  
  30. public static void main(String[] args) {

  31. final Optimize otz = new Optimize();

  32. Thread t1 = new Thread(new Runnable() {

  33. @Override

  34. public void run() {

  35. otz.doLongTimeTask();

  36. }

  37. },"t1");

  38. Thread t2 = new Thread(new Runnable() {

  39. @Override

  40. public void run() {

  41. otz.doLongTimeTask();

  42. }

  43. },"t2");

  44. t1.start();

  45. t2.start();

  46.  
  47. }

  48.  
  49.  
  50. }

打印:

 
  1. 当前线程开始:t1, 正在执行一个较长时间的业务操作,其内容不需要同步

  2. 当前线程开始:t2, 正在执行一个较长时间的业务操作,其内容不需要同步

  3.  
 
  1. 当前线程:t2, 执行同步代码块,对其同步变量进行操作

  2. 当前线程结束:t2, 执行完毕

  3.  
 
  1. 当前线程:t1, 执行同步代码块,对其同步变量进行操作

  2. 当前线程结束:t1, 执行完毕

解释:一个操作内,大部分操作不需要同步,异步即可满足,而只有其中一小步需要进行同步控制,比如修改数据库的一条数据,那么仅仅使用代码块对这个操作进行加锁即可。(数据库使用版本号也可以实现乐观锁,update时比对前一步查出来的版本号是否被修改,如果没被修改则执行修改,否则重试)。

 

死锁

多个线程相互持有对方的锁不释放,导致相互等待,出现死锁。

 

[java] view plain copy

  1. <code class="language-java">package com.jeff.base.sync006;  
  2.   
  3. /** 
  4.  * 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况 
  5.  * @author jeff 
  6.  * 
  7.  */  
  8. public class DeadLock implements Runnable{  
  9.   
  10.     private String tag;  
  11.     private static Object lock1 = new Object();  
  12.     private static Object lock2 = new Object();  
  13.       
  14.     public void setTag(String tag){  
  15.         this.tag = tag;  
  16.     }  
  17.       
  18.     @Override  
  19.     public void run() {  
  20.         if(tag.equals("a")){  
  21.             synchronized (lock1) {  
  22.                 try {  
  23.                     System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");  
  24.                     Thread.sleep(2000);  
  25.                 } catch (InterruptedException e) {  
  26.                     e.printStackTrace();  
  27.                 }  
  28.                 synchronized (lock2) {  
  29.                     System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");  
  30.                 }  
  31.             }  
  32.         }  
  33.         if(tag.equals("b")){  
  34.             synchronized (lock2) {  
  35.                 try {  
  36.                     System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");  
  37.                     Thread.sleep(2000);  
  38.                 } catch (InterruptedException e) {  
  39.                     e.printStackTrace();  
  40.                 }  
  41.                 synchronized (lock1) {  
  42.                     System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");  
  43.                 }  
  44.             }  
  45.         }  
  46.     }  
  47.       
  48.     public static void main(String[] args) {  
  49.           
  50.         DeadLock d1 = new DeadLock();  
  51.         d1.setTag("a");  
  52.         DeadLock d2 = new DeadLock();  
  53.         d2.setTag("b");  
  54.            
  55.         Thread t1 = new Thread(d1, "t1");  
  56.         Thread t2 = new Thread(d2, "t2");  
  57.            
  58.         t1.start();  
  59.         try {  
  60.             Thread.sleep(500);  
  61.         } catch (InterruptedException e) {  
  62.             e.printStackTrace();  
  63.         }  
  64.         t2.start();  
  65.     }  
  66.       
  67.   
  68.       
  69. }  
  70. </code>  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值