1.什么是线程安全:
官方的定义:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。
面试官接着问:
2.如何保证线程安全:
我回答了以下两个:
1、Synchronized:同步关键字,自动锁,悲观锁
synchronized锁住的是对象,可以锁类里面的obj对象,可以锁住this类对象,可以锁住该类的class对象(静态方法或者块的锁)
注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。即锁细化,此处可以引申一下锁的粗化与细化。
2、Lock:手动锁,其子类为RentrantLock,RentrantLock为独占锁,可分为公平锁和非公平锁。
接着介绍了常用的方法:
lock()方法,上锁
unlock()方法:解锁吗,通常加在finally块中,
trylock()方法,boolean型,尝试上锁,不会造成阻塞,其实tryLock()是可以进行设置等待的相应时间的。
这里可以引申一下公平锁和非公平锁。
3.接着他问了下我synchronized的底层实现,我当时懵了,一个关键字的底层实现确实没怎么注意过。
4.接着又问synchronized是可重入锁吗,什么是可重入锁?
synchronized是可重入锁,可重入的意思类似于递归。
所谓重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
synchronized 和 ReentrantLock 都是可重入锁。
可重入锁的意义在于防止死锁。
实现原理是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。
如果同一个线程再次请求这个锁,计数将递增;
每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。
5.集合方面:list哪些是线程安全的,arraylist为什么不是线程安全的?Arraylist什么时候进行第一次扩容?
默认是:arraylist默认初始容量为0,当添加第一个对象时,扩容为10.
几种list的区别就不在叙述。
arraylist举例说明为何不是线程安全:在两个线程对arraylist进行add()时,开始size()==0,A线程正在进行add(),在还没有完成之前,B线程也对arraylist进行add(),此时,size()就被加了两次size()==2,而arraylist中,只插入了一个数据,其中一个被覆盖掉了。如果是多个线程呢?
6、说一说Hashmap的实现
先分java7前后,java7以前数组+链表,通过对象的hashcode()找数组下标,equals()找链表中的具体位置.
Java7以后,数组+链表+红黑树,在java7的基础上,如果链表长度大于阈值
8,则自动转化为红黑树,理由:链表遍历效率O(n),红黑树遍历效率O(logN)
数组2^n长度存在,2倍大小扩容,负载因子是0.75,(注意:并非超过给定大小的075%就扩容,是根据hashmap中数组的实际长度来计算是否扩容的)
Hashmap允许key,value为null吗?
Key:只能存在一个null,多的覆盖
Value:可以有多个null值
引申:hashtable不允许有null的key值,接着可以讲一讲hashtable与hashmap,currenthashmap的区别
8.说一说Currenthashmap的实现
采取分段锁,用到sagment,默认可以最大并发线程为16个。详细另行查阅
i/o部分:一个编程题,大致意思,就是对一个文件夹的遍历,找出里面所有的java文件。
9.数据库方面:给出条件,编写sql,大致是求和,分组,排序