一道JAVA面试,线程安全和静态内部类
2016-04-05 22:52
218 查看
前言:4月1号去一家互联网公司面试,做了一份笔试。考察的内容也非常基础,但是里面却充满着各种各样的扩展。但是这份题我做得并不好,平时用框架什么的用多了,反而基础显得非常不扎实。凭着记忆写起最后一套题目。记一下,扎实一下自己的基础。
题目是要求我们寻找出里面的错误或不妥的地方。
1、线程安全:这是比较明显的,因为ArrayList是非线程安全的,所以在多进程进行的时候,而这个有两个线程共享了静态代码。所以线程安全问题是非常明显的。
2、数组下标溢出异常:
在还没有添加同步机制时,上面的代码会容易产生数据错误。
为了去除脏数据,就应该为代码添加同步机制。我先尝试第一种,同步方法。
我把run方法直接写成同步方法。但,仍然会出现脏数据!
这就证明,
在解释这个问题前,必需先清楚一些同步方法的原理
1、如果是一个非静态的同步函数的锁,锁对象是this对象。
2、如果是静态的同步函数的锁,锁对象是该类的字节码对象。
代码中是通过这一种方式去实现线程创建的。也就是说,有两个不同的。
我们在静态内部类中添加了一个
我尝试使用一种新的方法去创建线程,如图:
我先创建出一个实例h,然后再传过去给两个线程创建实例。这时候两个
所以,假如两个
同步块比较容易理解,主要就是关注锁对象。
有一个问题我一直都不是很明白,为什么说
代码
/** * declaration: * * author wenkangqiang * date 2016年4月1日 */ public class FankeTest { static class haha implements Runnable{ public haha(List<String> list) { // TODO Auto-generated constructor stub this.list = (ArrayList<String>)list; } @Override public void run() { while(log != null){ log = list.remove(0); //报下标错误 System.out.println(log); } } private List<String> list; private String log = ""; } public static void main(String[] args) { String[] strs = new String[]{"aa", "bb", "cc"}; ArrayList<String> list = new ArrayList<String>(Arrays.asList(strs)); Thread t1 = new Thread(new haha(list)); Thread t2 = new Thread(new haha(list)); t1.start(); t2.start(); } }
题目是要求我们寻找出里面的错误或不妥的地方。
1、线程安全:这是比较明显的,因为ArrayList是非线程安全的,所以在多进程进行的时候,而这个有两个线程共享了静态代码。所以线程安全问题是非常明显的。
2、数组下标溢出异常:
list.remove(0);在发现数组中已经没有数据的时候,会直接报下标溢出异常
IndexOutOfBoundsException(),并不会返回
null值给
log。
尝试修改:
增加判空判断,为循环写一个出口。为了观看方便,我为两个线程加了名字,并且在run方法中输出,为了避免处理速度过快而看不到效果,我可以在线程中添加sleep方法。
while(log != null){ if (list.size() > 0) { log = list.remove(0); try { //放慢线程 Thread.currentThread().sleep(1); System.out.println( Thread.currentThread().getName() + log); } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } }
在还没有添加同步机制时,上面的代码会容易产生数据错误。
OUTPUT: t2aa t1aa t1cc t2cc
为了去除脏数据,就应该为代码添加同步机制。我先尝试第一种,同步方法。
@Override public synchronized void run()
我把run方法直接写成同步方法。但,仍然会出现脏数据!
OUTPUT: t1aa t2aa t2cc t1cc
这就证明,
synchronized锁不住这个方法。
在解释这个问题前,必需先清楚一些同步方法的原理
1、如果是一个非静态的同步函数的锁,锁对象是this对象。
2、如果是静态的同步函数的锁,锁对象是该类的字节码对象。
Thread t1 = new Thread(new haha(list));
代码中是通过这一种方式去实现线程创建的。也就是说,有两个不同的。
Thread对象共享着一个
Runnable接口下的静态实例?这样说对吗?做一个测试了解一下。
我们在静态内部类中添加了一个
int i结果让人很意外,原本认为两个
new haha(list)会指向同一片内存,实际上我错了。每一个线程都享用这一个
haha实例,只有把
i设为
static才能出现我们想要的共享内存。
我尝试使用一种新的方法去创建线程,如图:
我先创建出一个实例h,然后再传过去给两个线程创建实例。这时候两个
Thread实例是共享一段代码的。根据Thread里面的代码可以看出,
Thread中
target属性是指向h对象的。
灵光一闪
现在终于知道里面是什么情况了。静态内部类是可以拥有多个实例的。因为我们两个线程分别拥有两个不同的haha对象。同步方法中的synchronized的锁分别是两个haha对象的。所以这时候,两个线程根本没有被锁住。
所以,假如两个
Thread实例使用同一个
haha对象,这个锁就会有用了!
同步块比较容易理解,主要就是关注锁对象。
总结
首先要承认,这是一道不普通的题。我是一边写博客,一边解决问题的。此前看来对静态内部类有点误解了,认为它只能创建一个实例。对比其与非静态内部类,就可以很容易理解它的异同了。线程锁对象及同步原理也是解决这个题目最关键的地方。有一个问题我一直都不是很明白,为什么说
Arraylist是非线程安全的呢?如何从内存角度分析线程安全问题呢?
相关文章推荐
- 面试题26 复杂链表的复制
- 数据库基本概念类 面试题
- [leetcode-328]Odd Even Linked List
- 码农小汪-设计模式之-策略模式
- Oralce数据库的优化(面试必问题)
- 让别人舒服的程度决定你路的高度
- 让别人舒服的程度,决定你路的宽度和高度
- 10道高频面试题整理~
- 结构体位制
- 一个程序员的孤独
- LeetCode(25)-symmetric tree
- 剑指offer面试题 求数组中只出现一次的数字
- [置顶] Android开发之数组类的面试题目,android工程师java程序员必备
- 捷通华声面试总结(待更新)
- Android面试记录
- 前端面试总结(转载请标明)
- 程序员必备注释
- SQL tuning 类 的面试题
- View事件面试总结21问21答。
- [leetcode-331]Verify Preorder Serialization of a Binary Tree