打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java线程同步与单例模式的结合学习

最近接触了一个项目的业务,是web客户端执行的备份和还原操作问题,当一个客户端点击了备份或还原操作时,在其它的web客户端则不能同时进行备份或还原,直到第一个客户端执行完全(需要5个小时),才能进行备份还原操作,并且这个项目是一个重构项目,不能影响之前的业务。

         在这里我们采用的是线程同步+单例的处理办法来解决的,改最少的代码最终解决问题,自己也好好的学习了一下java线程同步和单例,闲来无事,自己对线程这一块进行了整理,把自己的一点收获写下来。

 

1、  A线程在访问单例service的同步方法a的时候,其他线程能否访问service的同步方法a?同步方法b?非同步方法c?

2、  两个以上线程同时访问同一个对象的同一个变量的情况下,会出现什么情况?

3、  当service不是单例(普通实例)的情况下,A线程在访问service实例的同步方法a的时候,其他线程能否访问service实例的同步方法a?同步方法b?非同步方法c?

 

带着上面的问题,直接贴代码

下面是一个单例类,不用多做介绍:

 1 package treadTest.threadAccess; 2  3 import java.util.ArrayList; 4 import java.util.List; 5  6  7 /** 8  *  9  * @author yhd10  * 单例类11  */12 public class SingletonService {13     private static SingletonService service;14     private List<Integer> list;15     List<Integer> newlist = new ArrayList<Integer>();16     17     18     private SingletonService(){}19     20 21     public static SingletonService getInstance(){22         if(null == service){23             service = new SingletonService();24         }25         return service;26     }27         28     29     //同步方法A30     public synchronized  void testA(){31         try {32             System.out.println("A 在执行" + Thread.currentThread());33             Thread.sleep(5000);34         } catch (InterruptedException e) {35             e.printStackTrace();36         }37     }38     39     //同步方法B40     public synchronized  void testB(){41         try {42             System.out.println("B 在执行" + Thread.currentThread());43             Thread.sleep(5000);44         } catch (InterruptedException e) {45             e.printStackTrace();46         }47     }48     49     //非同步方法C50     public void testC(){51         System.out.println("C 在执行" + Thread.currentThread());52         try {53             Thread.sleep(5000);54         } catch (InterruptedException e) {55             e.printStackTrace();56         }57     }58     59     //非同步方法C160     public void testC1(){61         System.out.println("C1 在执行" + Thread.currentThread());62     }63     64     65     public void setList(List<Integer> list){66         this.list = list;67     }68     //非同步方法D 传入参数69     public void testD(){70         71         System.out.println("D 在执行"+ Thread.currentThread());72         73         for(int num:list){74             System.out.println(num +" "+Thread.currentThread() + this.toString());75             newlist.add(num);76         }77         System.out.println(newlist.toString()+"list的长度"+newlist.size());78     }79 80 }

 

下面是一个单例线程的测试类,主要是对上面的单例进行各种线程访问方法的测试

 1 package treadTest; 2  3 /** 4  *  5  * @author yhd 6  * 单例模式线程测试类 7  */ 8  9 public class SingletonServiceTest extends Thread{10     //单例模式下调用同步方法的时候,该单例会被锁住,其他线程想调用11     //该单例的其它同步方法的时候就会进行同步,如果调用的不是单例方法则不受影响12     SingletonService service = SingletonService.getInstance();13     static int tag = 1;14     15     @Override16     public void run() {17         if(tag == 1){18             service.testA();19         }20         else if(tag == 2){21             service.testB();22         }23         else{24             service.testC();25         }26     }27     //这是使用代码块来实现的,相应的也学习了一下同步代码块和同步方法的使用28     //这里的使用的是方法c和c1,这两个都不用同步了,因为这里已经对service同步了29 //    public void run(){30 //        synchronized (service) {31 //            if(tag == 1){32 //                service.testC();33 //            }34 //            else{35 //                service.testC1();36 //            }37 //        }38 //    }39     40     41     42     public static void main(String args[]){43 44         new SingletonServiceTest().start();45         //第二个线程能否访问该单例的同一个同步方法A46         new SingletonServiceTest().start();47         //下面主要是让main线程歇一会儿,否则main执行完了才会启动子线程,导致tag为3,没有意义48         try {49             Thread.sleep(10);50         } catch (InterruptedException e) {51             e.printStackTrace();52         }53         tag = 2;54         //第三个线程能否访问该单例的另外一个同步方法B55         new SingletonServiceTest().start();56         try {57             Thread.sleep(10);58         } catch (InterruptedException e) {59             e.printStackTrace();60         }61         tag = 3;62         //第四个线程能否访问该单例的另外一个非同步方法C63         new SingletonServiceTest().start();64     }65 }

 

运行结果:

A 在执行Thread[Thread-1,5,main]
C 在执行Thread[Thread-3,5,main]
B 在执行Thread[Thread-2,5,main]
A 在执行Thread[Thread-0,5,main]

一开始是出现A和C,过5秒左右出现B,再过5秒出现A

说明:A线程在访问单例service的同步方法a的时候,其他线程不能否访问service的同步方法a和同步方法b,但是能访问非同步方法c

 

继续第二个问题的测试,直接上代码:

 1 package treadTest; 2  3 import java.util.ArrayList; 4 import java.util.List; 5  6  7 //单例模式下,多个线程调用单例的用一个非同步方法,会出现什么情况?会混乱么 8 public class ServiceTest2 extends Thread{ 9     static SingletonService service = SingletonService.getInstance();10     static List<Integer> list1;11     static List<Integer> list2;12     13     @Override14     public void run() {15         service.testD();16     }17 18     19     public static void main(String args[]){20         list1 = new ArrayList<Integer>();21         //给list1添加1-1000的数据22         for(int i=0;i<1000;i++){23             list1.add(i);24         }25 26         list2 = new ArrayList<Integer>();27         //给list2添加1000-2000的数据28         for(int i=1001;i<2000;i++){29             list2.add(i);30         }31         32         33         //给service中的list赋list134         service.setList(list1);35         //启动第一个线程36         new ServiceTest2().start();37         38         //同理 暂停main一会39         try {40             Thread.sleep(10);41         } catch (InterruptedException e) {42             e.printStackTrace();43         }44         45         //给service中的list赋list146         service.setList(list2);47         //启动第一个线程48         new ServiceTest2().start();49         50     }51     52     53     54 }

 

运行结果:

998 Thread[Thread-0,5,main]treadTest.SingletonService@61de33
999 Thread[Thread-0,5,main]treadTest.SingletonService@61de33

1747 Thread[Thread-1,5,main]treadTest.SingletonService@61de33
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ………………1538] list的长度1539 或者出异常
1748 Thread[Thread-1,5,main]treadTest.SingletonService@61de33

……

1998 Thread[Thread-1,5,main]treadTest.SingletonService@61de33
1999 Thread[Thread-1,5,main]treadTest.SingletonService@61de33
[0, 1, 2, 3, 4, 5……1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, ……1999]list的长度1999

 

newlist并没有是单例中方法传入的多个list而混乱  跟线程调用普通类的情形没有区别,结果说明:同时访问同一个对象的同一个变量的情况下会混乱,出现了线程安全的问题,如果想要

多个线程共享一个变量,那么可以采用ThreadLocal(线程局部变量),每个线程都有该变量的一个副本,互不影响

3、验证第三个问题

直接上代码:

类似于上面的单例类,只是改成了普通类而已

 1 package treadTest; 2 public class Service { 3  4     //同步方法A 5     public synchronized  void testA(){ 6         try { 7             System.out.println("A 在执行" + Thread.currentThread()); 8             Thread.sleep(5000); 9         } catch (InterruptedException e) {10             e.printStackTrace();11         }12     }13     14     //同步方法B15     public synchronized  void testB(){16         try {17             System.out.println("B 在执行" + Thread.currentThread());18             Thread.sleep(5000);19         } catch (InterruptedException e) {20             e.printStackTrace();21         }22     }23     24     //非同步方法C25     public void testC(){26         System.out.println("C 在执行"+ Thread.currentThread());27         try {28             Thread.sleep(5000);29         } catch (InterruptedException e) {30             e.printStackTrace();31         }32     }33 34 }

 

非单例类的测试类如下:与上面的测试类的区别就是把类的实例化放在了run方法中,从而使得每次都new一个对象

 1 package treadTest; 2  3  4 //单例模式下,该单例有两个同步方法,当一个线程调用单例的其中一个同步方法A的时候, 5 //则其他的线程的这个单例能不能调用这个A方法?另外一个同步方法B呢?非同步方法呢? 6 public class ServiceTest extends Thread{ 7     //单例模式下调用同步方法的时候,该单例会被锁住,其他线程想调用 8     //该单例的其它同步方法的时候就会进行同步,如果调用的不是单例方法则不受影响 9     Service service;10     static int tag = 1;11     12     13     14     15     16     17     @Override18     public void run() {19         service = new Service();20         if(tag == 1){21             service.testA();22         }23         else if(tag == 2){24             service.testB();25         }26         else{27             service.testC();28         }29     }30     31     public static void main(String args[]){32 33         new ServiceTest().start();34         new ServiceTest().start();35         try {36             Thread.sleep(10);37         } catch (InterruptedException e) {38             e.printStackTrace();39         }40         tag = 2;41         new ServiceTest().start();42         try {43             Thread.sleep(10);44         } catch (InterruptedException e) {45             e.printStackTrace();46         }47         tag = 3;48         new ServiceTest().start();49     }50 }

 

运行结果:

A 在执行Thread[Thread-0,5,main]
A 在执行Thread[Thread-1,5,main]
B 在执行Thread[Thread-2,5,main]
C 在执行Thread[Thread-3,5,main]

没有丝毫的延迟,说明当service是普通类的情况下,A线程在访问service实例的同步方法a的时候,其他线程可以访问service实例的所有同步和非同步方法

总结收获:

同步方法锁定的是调用改同步方法的实例,不允许同一个实例在不同的线程中访问实例中的同步方法。比如SingletonService的service实例,当线程a访问service的同步方法a,导致service被锁定,其它的线程想访问service的同步方法b的时候就会去尝试给service上锁,但是此时线程a已经锁定了service实例,所以其它线程无法访问service的同步方法了。但是其它线程可以访问非同步方法,非同步方法并不尝试上锁。

 

当不同线程中SingletonService的不同实例来访问同步方法的时候,此时只能保证同一个实例不能在不同的线程中访问同步方法,不能拒绝不同的实例访问各自实例的同步方法,因此不同实例访问互不影响

在项目的备份还原中用的就是多线程同步+单例,当一个客户端操作的时候,单例会被锁定,其它客户端在访问这个单例的同一个或其它同步方法的时候,都会等待第一个客户只需完毕。ok就这样解决了。O(∩_∩)O~

第一次发博,愚见……

附上一道华为的面试题,挺给力的,各位慢慢赏玩吧……

 1 package treadTest; 2 //输出结果是什么呢? 3 public class Thread1{ 4                      public static synchronized void main(String args[]){ 5                                   Thread t=new Thread(){ 6                                       public void run(){ 7                                                pong(); 8                                      } 9                                   };10                                  t.run();11                                 12                                   13                                   try {14                                     Thread.sleep(100);15                                 } catch (InterruptedException e) {16                                     e.printStackTrace();17                                 }18                                  System.out.print("ping");19                     }20                     static synchronized void pong(){21                                   System.out.print("pong");22                     }23                    24 }

 

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
多线程编程——实战篇(二)
java线程同步
Java多线程访问Synchronized同步方法的八种使用场景
单例模式
WCF实现一个单例
初学者第67节多线程之单例模式(十)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服