关于ThreadLocal的一道面试题(酷我公司)
2013年8月,本人那时候刚毕业来到了北京找工作,在网上投递了各种简历,也面试了很多家公司,遇到最大的问题就是:你什么时候毕业的呀?,做过什么项目呀?都将我拒之门外,但是我还是幸运总会来的,那天早上9点半的时候,接到电话,说叫我去面试,问了一下是什么公司?是酷我,感觉公司规模挺大的还可以,就很兴奋的跑去面试了,他们公司没有笔试,只有面试,有三轮面试,第一轮就是问你做过哪些项目,都遇到什么问题怎么解决的等,和技术不沾边,第二轮面试的时候就来了一个技术,问了很多关于技术上的问题,但是貌似都不深入,比如:怎样快速的查找到单链表中的倒数第k个元素(网上有答案,上数据结构课的时候老师说过的);找出两个升序序列的相同元素(普通方法肯定是可以的,但是不是他想要的答案,使用头指针和尾指针,时间复杂度是O(n));单列表的反转等这些问题。因为我是有准备去面试的,这些问题网上很是流传,所以早弄得滚瓜烂熟了,全部搞定,感觉他挺满意的,最后他又来一道题:
单例模式的定义?单例模式有哪些形式?怎样控制多线程访问单例模式?怎么提高多线程访问单例模式的性能?
其实总共是四个问题,都是循序渐进的:个人感觉这个问题我没有准备,但是我之前搞过这些:所以就开始回答了:
1.单例模式的定义很简单的:构造方法为private,定义一个static的自身实例变量,提供一个static的供外部访问对象的getInstance方法
2.单例模式有懒汉和饿汉模式:具体看下面的代码:
//比较:
//饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变
//懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的,因为多个线程可能创建出多个实例
//推荐使用第一种 //饿汉式:
class HungrySingleton{
private static HungrySingleton hungrysingleton = new HungrySingleton();
private HungrySingleton (){}
public HungrySingleton getInstance(){
return hungrysingleton;
}
} //懒汉式:
class FullSingleton{
private static FullSingleton fullsingleton = null;
public static synchronized FullSingleton getInstance(){
if(fullsingleton==null){
fullsingleton = new FullSingleton();
}
return fullsingleton;
}
}
3.我们看一下上面的代码:
对于饿汉模式是不存在多线程访问不安全的问题的,只有下面的懒汉模式有多线程访问的安全问题,所以我们在getInstance()方法前加入synchronized进行互斥操作即可
4.因为我们知道使用synchronized关键字来实现互斥,性能很低的,所以怎么提高性能呢?有的人可能说了,直接使用饿汉模式就可以了,但是现在我们就是用懒汉模式来实现,多线程访问,那就可以想到了Java中的ThreadLocal类了,就是和本线程相关的一个map集合,这样就很好理解了,但是他内部实现是很复杂的,要考虑到效率的问题,比如一个线程消亡的时候,线程持有的资源该怎么释放都是要特殊处理的,但是我们没必要考虑他那么深入,只要知道怎么用的就行了,下面就直接来看一下代码吧:
package cn.itcast.heima;
import java.util.Random;
public class ThreadLocalTest {
public static void main(String[] args){
//开启两个线程设置值
for(int i=0;i<2;i++){
new Thread(new Runnable(){
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" has put data :" + data);
MyThreadScopeData.getInstance().setName(data+"");
MyThreadScopeData.getInstance().setAge(data+"");
new A().get();
new B().get();
}
}).start();
}
}
//获取值
static class A{
public void get(){
System.out.println(Thread.currentThread().getName()+" : " + "Name:"+MyThreadScopeData.getInstance().getName());
System.out.println(Thread.currentThread().getName()+" : " + "Age:"+MyThreadScopeData.getInstance().getAge());
}
}
//获取值
static class B{
public void get(){
System.out.println(Thread.currentThread().getName()+" : " + "Name:"+MyThreadScopeData.getInstance().getName());
System.out.println(Thread.currentThread().getName()+" : " + "Age:"+MyThreadScopeData.getInstance().getAge());
}
}
}
class MyThreadScopeData{
private MyThreadScopeData(){}
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
//因为使用了ThreadLocal的方法,所以这里就不需要synchronized,这样效率就更高了
public static MyThreadScopeData getInstance(){
MyThreadScopeData instance = map.get();
if(instance == null){
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
代码中定义了两个线程,我们在MyThreadScopeData类中的getInstance()方法中我们使用到了ThreadLocal类,将对象实例和线程相关联上,运行结果:
我们从结果可以看到,两个线程产生的数据是一致的。这样四个问题就回答完了,其实我现在也不清楚这几个问题回答的怎么样,但是面试很顺利,所以我就分享一下我的答案!如有不正确的地方,希望给予指正,小弟不胜感激。
关于ThreadLocal的一道面试题(酷我公司)的更多相关文章
- PHP递归创建多级目录(一道面试题的解题过程)
今天看到一道面试题,要写出一个可以创建多级目录的函数: 我的第一个感觉就是用递归创建,具体思路如下: function Directory($dir){ if(is_dir($dir) || @mkd ...
- (转)从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节
背景:学习java的基础知识,每次回顾,总会有不同的认识.该文系转载 最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的, ...
- 关于一道面试题,使用C#实现字符串反转算法
关于一道面试题,使用C#实现字符串反转算法. 题目见http://student.csdn.net/space.php?do=question&ac=detail&qid=490 详细 ...
- 一道面试题:C++相比C#或者java的优势到底在哪里
被问到了这样一道面试题,当时就懵了,内心一直觉得C++肯定在很多方面要比C#或者java要牛b的. 但是真的不知道怎么回答. 问题是:你以前一直做得是.NET相关项目,现在为什么找C++开发相关工作呢 ...
- PHP递归创建多级目录(一道面试题的解题过程)(转)
今天看到一道面试题,要写出一个可以创建多级目录的函数: 我的第一个感觉就是用递归创建,具体思路如下: function Directory($dir){ if(is_dir($dir) || @m ...
- 关于Java类加载双亲委派机制的思考(附一道面试题)
预定义类加载器和双亲委派机制 JVM预定义的三种类型类加载器: 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面 ...
- 一道面试题比较synchronized和读写锁
一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用, ...
- 一道面试题:按照其描述要求用java语言实现快速排序
回来想了想,写出了如下的程序: /** * 一道面试题,按照其描述要求进行快速排序(英文的,希望理解是对的..) * 要求:和一般的快速排序算法不同的是,它不是依次交换pivot和左右元素节点(交换2 ...
- <转>一道面试题比较synchronized和读写锁
一.科普定义(原文:http://903497571.iteye.com/blog/1874752) 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步 ...
随机推荐
- java序列化对象为什么要定义serialversionUID值?
SerialVersionUid,简言之,其目的是序列化对象版本控制,有关各版本反序列化时是否兼容.如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassExcep ...
- Python常用三方库安装
//首先更新pip python -m pip install --upgrade pip //一个类似Matlab的Plot绘制数据图的库. python -m pip install matplo ...
- Packet for query is too large (1986748 > 1048576). You can change this value on the server by 异常
场景:mybatis动态拼接,批量添加数据,因为数据太多,凭借过多,导致MySql数据库中插入大于1m的数据时,提示该异常. 解决办法:修改mysql的属性 max_allowed_packet即可. ...
- mysql 毫秒时间转换
当在数据库中存储的时间类型为bigint类型时,及时间的毫秒数 java中: new DATE().gettime();//获取时间的毫秒数 当需要将毫秒数转化为时间的时候 mysql中: FROM ...
- C之输入输出函数(3) -- 请使用sscanf()
#include <stdio.h> int fscanf(FILE *__restrict__stream, const char *__restrict__format-string, ...
- asp.net MVC遇到的问题
参考:http://blog.csdn.net/chenqiangdage/article/details/48996101 asp.netMVC 如何解决“上下文模型已在数据库创建后发生更改” 问题 ...
- PHP ftp_put() 函数
定义和用法 ftp_put() 函数上传本地一个文件到 FTP 服务器上. 如果成功,该函数返回 TRUE.如果失败,则返回 FALSE. 语法 ftp_put(ftp_connection,remo ...
- noip1998 提高组t3 挖地雷
题目背景 NOIp1996提高组第三题 题目描述 在一个地图上有N个地窖(N<=20),每个地窖中埋有一定数量的地雷.同时,给出地窖之间的连接路径.当地窖及其连接的数据给出之后,某人可以从任一处 ...
- (转)OpenFire源码学习之五:用户登录
转:http://blog.csdn.net/huwenfeng_2011/article/details/43413377 登陆 登陆认证,客户端发送认SASL证消息: <auth mecha ...
- CSS:CSS 链接
ylbtech-CSS:CSS 链接 1.返回顶部 1. CSS 链接 不同的链接可以有不同的样式. 链接样式 链接的样式,可以用任何CSS属性(如颜色,字体,背景等). 特别的链接,可以有不同的样式 ...