并发
线程有哪些状态
Java线程分成六种状态
线程池的核心参数
1.corePoolSize 核心线程数目
2.maximumPoolSize 最大线程数目
3.keepAliveTime 生存时间
4.unit 时间单位
5.workQueue
6.threadFactory 线程工厂
7.handler 拒绝策略
sleep VS wait
- 共同点:wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态。
- 方法归属不同
- sleep方法是Thread的静态方法
- 而wait(),wait(long)是Object的成员方法,每个对象都有
- 醒来时间不同
- 执行sleep(long)和wait(long)的线程都会在等待相同毫秒后醒来
- wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒就一直等待下去
- 都可以被打断唤醒
- 锁特性不同
- wait方法的调用必须先获取wait对象的锁,而sleep不用
- wait方法执行后会释放对象锁,允许其他线程获得该对象锁
- sleep如果在synchronized代码块中执行,并不会释放对象锁
lock 和synchronized的区别
- 语法层面:
- synchronized是关键字,源码在jvm,是C++实现
- lock是接口,由jdk提供,Java实现
- 使用synchronized时,退出同步代码块时会自动释放锁,lock需要调用unlock手动释放锁
- 功能层面
- 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
- lock提供了很多synchronized不具备的功能,例如获取等待状态、公平锁、可打断、可超速、多条件变量
- lock有适合不同的场景使用,如ReentrantLock,ReentrantReadWriteLock(读多写少)
- 性能层面
- 在没有竞争或较小时,synchronized做了很多优化、如意向锁、轻量级锁,性能不赖
- 在竞争激烈时,lock的实现通常能提供更好的性能
volatile能否保证线程安全
- 1.线程安全考虑三个方面:可见性、有序性、原子性
- 可见性:一个线程对共享变量修改,另一个线程能看到最新的结果
- 有序性:一个线程内代码按编写顺序执行
- 原子性:一个线程内多行代码以一个整个执行,期间不能有其他线程的代码插队
- 2.volatile能保证可见性、有序性,但不保证原子性,在多线程时可能会在执行了一半时切换线程,共享变量交错覆盖
{
int s = sum;
s -= 5;
sum = s;
return sum;
}
{
int s = sum;
s += 5;
sum = s;
return sum;
}
- 可见性问题:是JIT优化更改了热点代码(常调用的方法,大量循环的代码),可以添加参数-Xint表示只需要翻译成机器码不用jit优化。
- 1.悲观锁的代表是synchronized和 Lock锁
- 其核心思想是【线程只有占有了锁,才能去操作共享变量,每次只有一个线程占锁成功,获取锁失败的线程,都得停下来等待】
- 线程从运行到阻塞、再从阻塞到唤醒,涉及线程上下文切换,如果频繁发生,影响性能
- 实际上,线程在获取synchronized和Lock锁时,如果锁已被占用,都会做几次重试操作,减少阻塞的机会
- 2.乐观锁的代表是AtomicInteger,使用cas(compareAndSet——对比并修改)(一般配合volatile使用)来保证原子性
- 其核心思想是【无需加锁,每次只有一个线程能成功修改共享变量,其它失败的线程不需要停止,不断重试直至成功】
- 由于线程一直运行,不需要阻塞,因此不涉及线程上下文切换
- 它需要多核cpu支持,且线程数不应超过cpu核数
Hashtable_vs_ConcurrentHashMap
-
Hashtable 与ConcurrentHashMap都是线程安全的Map集合
-
Hashtable并发度低,整个Hashtable对应一把锁,同一时刻,只能有一个线程操作它
-
1.8之前ConcurrentHashMap使用了Segment+数组+链表的结构,每个Segment对应一把锁,如果多个线程访问不同的Segment,则不会冲突
-
1.8开始ConcurrentHashMap将数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突
-
1.7:ConcurrentHashMap底层数据结构是Segment+数组+链表
-
1.8:ConcurrentHashMap底层数据结构是数组+(链表 | 红黑树)
-
1.7:ConcurrentHashMap初始化直接创建了16大小的数组
-
1.8:ConcurrentHashMap是在第一次使用put方法才创建
-
1.7:ConcurrentHashMap扩容机制是超过3/4扩容
-
1.8:ConcurrentHashMap扩容机制是满3/4就扩容
-
Hashtable初始数组大小是11、扩容阈值是超过3/4,扩容数量是原容量*2+1
谈一谈对ThreadLocal的理解
- ThreadLocal可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题
- ThreadLocal同时实现了线程内的资源共享
- 其原理是,每个线程内有一个ThreadLocalMap类型的成员变量,用来存储资源对象
- 调用set方法,就是以ThreadLocal自己作为key,资源对象作为value,放入当前线程的ThreadLocalMap集合中
- 调用get方法,就是以ThreadLocal自己作为key,到当前线程中查找关联的资源值
- 调用remove方法,就是以ThreadLocal自己作为key,移除当前线程关联的资源值