关于ThreadLocal的一道面试题(酷我公司)

http://blog.csdn.net/jiangwei0910410003/article/details/20448895#comments

2013年8月,本人那时候刚毕业来到了北京找工作,在网上投递了各种简历,也面试了很多家公司,遇到最大的问题就是:你什么时候毕业的呀?,做过什么项目呀?都将我拒之门外,但是我还是幸运总会来的,那天早上9点半的时候,接到电话,说叫我去面试,问了一下是什么公司?是酷我,感觉公司规模挺大的还可以,就很兴奋的跑去面试了,他们公司没有笔试,只有面试,有三轮面试,第一轮就是问你做过哪些项目,都遇到什么问题怎么解决的等,和技术不沾边,第二轮面试的时候就来了一个技术,问了很多关于技术上的问题,但是貌似都不深入,比如:怎样快速的查找到单链表中的倒数第k个元素(网上有答案,上数据结构课的时候老师说过的);找出两个升序序列的相同元素(普通方法肯定是可以的,但是不是他想要的答案,使用头指针和尾指针,时间复杂度是O(n));单列表的反转等这些问题。因为我是有准备去面试的,这些问题网上很是流传,所以早弄得滚瓜烂熟了,全部搞定,感觉他挺满意的,最后他又来一道题:


单例模式的定义?单例模式有哪些形式?怎样控制多线程访问单例模式?怎么提高多线程访问单例模式的性能?

其实总共是四个问题,都是循序渐进的:个人感觉这个问题我没有准备,但是我之前搞过这些:所以就开始回答了:


1.单例模式的定义很简单的:构造方法为private,定义一个static的自身实例变量,提供一个static的供外部访问对象的getInstance方法


2.单例模式有懒汉和饿汉模式:具体看下面的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //比较:  
  2. //饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变  
  3. //懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的,因为多个线程可能创建出多个实例  
  4. //推荐使用第一种   
  5.   
  6. //饿汉式:  
  7. class HungrySingleton{  
  8.     private static HungrySingleton hungrysingleton = new HungrySingleton();  
  9.     private HungrySingleton (){}  
  10.     public HungrySingleton getInstance(){  
  11.         return hungrysingleton;  
  12.     }  
  13. }   
  14.   
  15. //懒汉式:  
  16. class FullSingleton{  
  17.     private static FullSingleton fullsingleton = null;  
  18.     public static synchronized FullSingleton getInstance(){  
  19.          if(fullsingleton==null){  
  20.              fullsingleton = new FullSingleton();  
  21.          }  
  22.         return fullsingleton;  
  23.     }  
  24. }   

3.我们看一下上面的代码:

对于饿汉模式是不存在多线程访问不安全的问题的,只有下面的懒汉模式有多线程访问的安全问题,所以我们在getInstance()方法前加入synchronized进行互斥操作即可


4.因为我们知道使用synchronized关键字来实现互斥,性能很低的,所以怎么提高性能呢?有的人可能说了,直接使用饿汉模式就可以了,但是现在我们就是用懒汉模式来实现,多线程访问,那就可以想到了Java中的ThreadLocal类了,就是和本线程相关的一个map集合,这样就很好理解了,但是他内部实现是很复杂的,要考虑到效率的问题,比如一个线程消亡的时候,线程持有的资源该怎么释放都是要特殊处理的,但是我们没必要考虑他那么深入,只要知道怎么用的就行了,下面就直接来看一下代码吧:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package cn.itcast.heima;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class ThreadLocalTest {  
  6.       
  7.     public static void main(String[] args){  
  8.         //开启两个线程设置值  
  9.         for(int i=0;i<2;i++){  
  10.             new Thread(new Runnable(){  
  11.                 @Override  
  12.                 public void run() {  
  13.                     int data = new Random().nextInt();  
  14.                     System.out.println(Thread.currentThread().getName()+" has put data :" + data);  
  15.                     MyThreadScopeData.getInstance().setName(data+"");  
  16.                     MyThreadScopeData.getInstance().setAge(data+"");  
  17.                     new A().get();  
  18.                     new B().get();  
  19.                 }  
  20.                   
  21.             }).start();  
  22.         }  
  23.     }  
  24.       
  25.     //获取值  
  26.     static class A{  
  27.         public void get(){  
  28.             System.out.println(Thread.currentThread().getName()+" : " + "Name:"+MyThreadScopeData.getInstance().getName());  
  29.             System.out.println(Thread.currentThread().getName()+" : " + "Age:"+MyThreadScopeData.getInstance().getAge());  
  30.         }  
  31.     }  
  32.       
  33.     //获取值  
  34.     static class B{  
  35.         public void get(){  
  36.             System.out.println(Thread.currentThread().getName()+" : " + "Name:"+MyThreadScopeData.getInstance().getName());  
  37.             System.out.println(Thread.currentThread().getName()+" : " + "Age:"+MyThreadScopeData.getInstance().getAge());  
  38.         }  
  39.     }  
  40.   
  41. }  
  42.   
  43. class MyThreadScopeData{  
  44.       
  45.     private MyThreadScopeData(){}  
  46.       
  47.     private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();  
  48.     //因为使用了ThreadLocal的方法,所以这里就不需要synchronized,这样效率就更高了  
  49.     public static MyThreadScopeData getInstance(){  
  50.         MyThreadScopeData instance = map.get();  
  51.         if(instance == null){  
  52.             instance = new MyThreadScopeData();  
  53.             map.set(instance);  
  54.         }  
  55.         return instance;  
  56.     }  
  57.       
  58.     private String name;  
  59.     private String age;  
  60.       
  61.     public String getName() {  
  62.         return name;  
  63.     }  
  64.     public void setName(String name) {  
  65.         this.name = name;  
  66.     }  
  67.     public String getAge() {  
  68.         return age;  
  69.     }  
  70.     public void setAge(String age) {  
  71.         this.age = age;  
  72.     }  
  73.       
  74.       
  75. }  
代码中定义了两个线程,我们在MyThreadScopeData类中的getInstance()方法中我们使用到了ThreadLocal类,将对象实例和线程相关联上,运行结果:



我们从结果可以看到,两个线程产生的数据是一致的。这样四个问题就回答完了,其实我现在也不清楚这几个问题回答的怎么样,但是面试很顺利,所以我就分享一下我的答案!如有不正确的地方,希望给予指正,小弟不胜感激。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值