Java线程同步与单例模式的结合学习
2013-05-07 10:49
295 查看
最近接触了一个项目的业务,是web客户端执行的备份和还原操作问题,当一个客户端点击了备份或还原操作时,在其它的web客户端则不能同时进行备份或还原,直到第一个客户端执行完全(需要5个小时),才能进行备份还原操作,并且这个项目是一个重构项目,不能影响之前的业务。
在这里我们采用的是线程同步+单例的处理办法来解决的,改最少的代码最终解决问题,自己也好好的学习了一下java线程同步和单例,闲来无事,自己对线程这一块进行了整理,把自己的一点收获写下来。
1、 A线程在访问单例service的同步方法a的时候,其他线程能否访问service的同步方法a?同步方法b?非同步方法c?
2、 两个以上线程同时访问同一个对象的同一个变量的情况下,会出现什么情况?
3、 当service不是单例(普通实例)的情况下,A线程在访问service实例的同步方法a的时候,其他线程能否访问service实例的同步方法a?同步方法b?非同步方法c?
带着上面的问题,直接贴代码
下面是一个单例类,不用多做介绍:
下面是一个单例线程的测试类,主要是对上面的单例进行各种线程访问方法的测试
运行结果:
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
继续第二个问题的测试,直接上代码:
运行结果:
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、验证第三个问题
直接上代码:
类似于上面的单例类,只是改成了普通类而已
非单例类的测试类如下:与上面的测试类的区别就是把类的实例化放在了run方法中,从而使得每次都new一个对象
运行结果:
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~
第一次发博,愚见……
附上一道华为的面试题,挺给力的,各位慢慢赏玩吧……
在这里我们采用的是线程同步+单例的处理办法来解决的,改最少的代码最终解决问题,自己也好好的学习了一下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 yhd 10 * 单例类 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 //同步方法A 30 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 //同步方法B 40 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 //非同步方法C 50 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 //非同步方法C1 60 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 @Override 16 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 //第二个线程能否访问该单例的同一个同步方法A 46 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 //第三个线程能否访问该单例的另外一个同步方法B 55 new SingletonServiceTest().start(); 56 try { 57 Thread.sleep(10); 58 } catch (InterruptedException e) { 59 e.printStackTrace(); 60 } 61 tag = 3; 62 //第四个线程能否访问该单例的另外一个非同步方法C 63 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 @Override 14 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赋list1 34 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赋list1 46 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 //同步方法B 15 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 //非同步方法C 25 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 @Override 18 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 }
相关文章推荐
- Java线程同步与死锁、生产者消费者模式以及任务调度等
- java中结合单例模式,控制单例类的某方法在被多线程调用时,只被1个线程调用执行,执行完毕后才被另一线程调用
- 学习和理解JAVA线程同步--生产者与消费者例子
- java----------线程学习同步锁
- JAVA线程同步——消费者和生产者模式
- java多线程学习总结之四:线程的同步
- Java学习之线程锁--单例模式--synchronized
- 【Java学习】线程的同步-synchronized的理解
- java中结合单例模式,控制单例类的某方法在被多线程调用时,只被1个线程调用执行,执行完毕后才被另一线程调用
- java线程学习——线程安全(同步)
- Java学习之线程锁--单例模式写法--synchronized
- 黑马程序员——学习日记12 java线程同步
- Android(java)学习笔记70:同步中的死锁问题以及线程通信问题
- Java学习之——线程非安全单利模式到线程安全单利模式
- 【Java并发学习】之线程的同步
- Java新技术---线程学习之常用同步工具类
- 由单例模式的优化,引出的java线程数据同步和类加载顺序知识点总结
- Netty学习之旅------线程模型前置篇Reactor反应堆设计模式实现(基于java.nio)
- 学习和理解JAVA线程同步--生产者与消费者例子
- java学习之协调同步的线程