几个基础问题
cpu 核心和线程数
以前
6个核心数,一个核一个线程 1:1
单核技术,1和核心1个线程
现在
超线程技术1:2 6个核心数 12 个线程
已经发展4核,6核技术
CPU 轮转机制
进程,操作系统管理的最小单元。线程cpu调度的最小单元
进程> 线程,1个进程最少1个线程。只要线程还存活,进程就活着
进程A{线程1,线程2...}
进程B{}挂掉
操作系统调度通过cpu执行的时候会在进程之间不停的切换线程执行,「算法RR调度」
每个线程的切换,都是上下文的切换,比较耗费时间周期,而且切换过程需要存取临时数据
这种机制也叫做(随机切换机制)
并发和并行
并行:四个跑道,四个运动员同时跑
并发:吞吐量。多个跑道10s 钟有多少车跑过去
高并发意义:如 6核12线程---> (适当线程数不要cpu过累)
Java 默认就是多线程的
Thread,Runnable,Callable(FutureTask,带有返回值) 三种方式
后面两个都会要放在Thread
真正的线程:调用thread start() -->native 的start ->c++ 调用系统,再返回到run方法中
如何控制线程结束
run方法用interrupt 进行通知,while循环判断是否interrupted 结束run
如何控制线程按顺序执行?t1 t2
join 如 t1.join 获取执行权,cpu调度会切换到t1
Java 能不能指定cpu执行那个线程
不能 java做不到,只能通过c 去掉用核心API指定才行
项目开发中,Java中你会考虑线程的优先级吗?
不考虑,因为线程优先级依赖于平台,所以这个优先级无法对号入座,无法做到想象的中优先级,不稳定,风险高,
如:java有10个优先级,而系统只有2-3级别,无法对应
Sleep 和 Wait有什么区别?
sleep 是休眠,休眠时间一过才有资格运行,仅仅是有资格执行,什么时候执行,取决与系统的调度
wait 是等待,需要人家来唤醒,被唤醒后,才有资格,也是不一定马上执行,什么时候执行,取决于系统调度
sleep 无条件休眠。wait是某些原因和条件需要等待(系统资源不足等)
java 为什么不能强制中断线程,怎么中断
不推荐使用stop,这种方式枪占式,
建议用interrupt 这种是协作的,并不一定马上中断,通知对方,对方判断条件中断
当执行sleep的时候,捕获出现异常,interrupt 标记被清除,可以在捕获异常再次发出interrupt()
如何让出当前线程的执行权
yield 方法 让出执行权
sleep 和 wait那个函数会清楚标记
sleep 在捕获异常之前会清除中断标记
锁
分类:内置锁,显示锁
-> synchronized (天生可重入锁,原子操作,非公平锁)
内置锁,
持有锁xxxx.class == 类锁 ,static 静态锁
对象锁 this == 对象锁,没有static
锁在synchronize 修饰的整个块或者方法结束才会释放锁
public class GPSEngine {
private static GPSEngine gpsEngine;
public static synchronized GPSEngine getInstance() {
if (gpsEngine == null) {
// Thread-1 释放CPU执行器 Thread-0来执行了 结果 new GPSEngine 此时Thread-1获得执行资格 有 new
gpsEngine = new GPSEngine();
}
return gpsEngine;
}
-> 「比在方法上枷锁效率高一些」
/*public static GPSEngine getGpsEngine() {
if (null == gpsEngine) {
synchronized (GPSEngine.class) { // 类锁 还没有new GPSEngine == 没有this
if (null == gpsEngine) {
gpsEngine = new GPSEngine();
}
}
}
return gpsEngine;
}*/
}
wait 或者 notify 必须有锁synchronize包裹
等待区域wait() 获取对象的锁,所以
synchronize(持有的锁 对象锁){
wait() 内部会释放锁
}
this 锁,统一把锁
通知区域notify() 获取对象的锁,所以
synchronize(持有的锁 对象锁){
notify() 按道理拿不到this的锁了,因为wait() 内部释放了锁
}
非常明确的时候notify(). 有多个锁wait()多个的时候,不确定是哪个,所以开源框架一般用notifyAll() 唤醒所有
-> Lock 显示锁 开发者自己控制(公平锁)
ReentrantLock 递归多次调用等可重复拿锁
线程的状态
显示锁Lock 不会进入阻塞状态,而是进入等待,底层实现,LockSuppoer.park-----LockSupport.unpark----> 一般是主动,资源不足等
而synchronize 才会进入阻塞---> 一般是被动进入
死锁: 两个或者两个以上进程彼此通信,竞争资源,造成一种阻塞,弱没有外力作用,无法推进下去。
造成死锁满足的条件->
- 操作者M个(M>=2),争夺多个资源N(N>=2),N<=M
- 争夺资源的顺序不对
- 拿到资源不放手
活锁:
线程 A,B 锁1,2 (都拿到执行)
A(持有1)<尝试2>--()()--(持有1)<尝试2>--()()--(持有1)<尝试2>...
B(持有2)<尝试1>--()()--(持有2)<尝试1>--()()--(持有2)<尝试1>...
拿不到就一致执行尝试
sleep 让出CPU 不会释放锁
ThreadLock
class Thread{
ThreadLocal.ThreadLocalMap threadLocals;
}
class ThreadLocalMap{
Entry[] table
}
class Entry{
ThreadLocal>k, Object v
}
每个ThreadLocal 保存了一份副本
// 给每个线程进行了隔离, 相当于 给每一个线程 Copy了一份副本一样
public class TestThreadLocal {
static ThreadLocal threadLocal = new ThreadLocal() {
@Override
protected String initialValue() {
return "雄霸";
}
};
private static class StudentThread extends Thread {
@Override
public void run() {
super.run();
// 首次拿 雄霸
// 非常明确 1.获取当前是那个线程 2.ThreadLocalMap
threadLocal.get(); // 雄霸
String threadName = currentThread().getName();
threadLocal.set("步惊云");
// get 永远都是再 StudentThread 线程修改的的
System.out.println("threadName:" + threadName + " get:" + threadLocal.get()); // 步惊云
}
}
private static class PersonThread extends Thread {
@Override
public void run() {
super.run();
String threadName = currentThread().getName();
threadLocal.get(); // 雄霸
threadLocal.set("秦霜");
// get 永远都是再 PersonThread 线程修改的的
System.out.println("threadName:" + threadName + " get:" + threadLocal.get()); // 秦霜
}
}
private static class WorkderThread extends Thread {
@Override
public void run() {
super.run();
String threadName = currentThread().getName();
System.out.println("threadName:" + threadName + " get:" + threadLocal.get()); // 雄霸
}
}
public static void main(String[] args) {
new StudentThread().start();
new PersonThread().start();
new WorkderThread().start();
String threadName = Thread.currentThread().getName();
System.out.println("threadName:" + threadName + " get:" + threadLocal.get());
}
}
结果:
threadName:Thread-0 get:步惊云
threadName:Thread-1 get:秦霜
threadName:Thread-2 get:雄霸
threadName:main get:雄霸
CAS 原子操作
Auomic** 类,synchronize 比较重
CAS 原理------乐观锁,先操作,比较如果被更改,不行自旋重算
利用现代处理器支持CAS指令
循环这个操作直到成功为止
compare and swap 要么全部完成,要么全部不完成
synchronize ------悲观锁,总有刁民想害朕,回阻塞,会有上下文切换,先上了锁
问题:
ABA 问题
线程1 A if A被改了-----> B
线程2 跑的块 A------>C------> A 那么中间变化了一次 线程1不知道
解决:加一个版本戳标记
AtomicMarkableReference,AtomicStampedReference
开销问题
多个线程激烈竞争,就会大量不断重试,就会出现开销问题
只能保证一个共享变量原子操作
解决: AtomicReference 多个共享变量打包到一个里面操作
cas不适合多个操作更适合sync
sync{
甲,乙,丙,丁
}
jdk 自带
更新基本类型 AtomicInteger,AtomicBoolean,AtomicLong
更新数组类型 AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新引用类型AtomicMarkableReference,AtomicStampedReference,AtomicReference
阻塞队列
BlockingQueue (java.util.concurrent)
BlockingDeque
TransferQueue
接口
ArrayBlockingQueue (java.util.concurrent)
数组实现的 ,有界阻塞
LinkedBlockingDeque (java.util.concurrent)
链表双向阻塞
SynchronousQueue (java.util.concurrent)
不存储元素的阻塞(数据直接传递)
DelayQueue (java.util.concurrent)
优先级队列,无界
LinkedTransferQueue (java.util.concurrent)
链表实现,无界, transfer 当正在获取元素,这个时候放入的元素,直接交给获取元素使用
LinkedBlockingQueue (java.util.concurrent)
链表实现,有界
PriorityBlockingQueue (java.util.concurrent)
优先队列 ,无界(可以扩容)
线程池
AsyncTask 就是线程池的实现
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
策略:
RejectedExecutionHandler 拒绝策略
DiscardOldestPolicy
直接丢弃最老的
AbortPolicy
不做任何配置,直接抛出异常
CallerRunsPolicy
如果当前主线程不断提任务,线程池满了无法执行,交给提交任务的执行。你行你上
DiscardPolicy
最新提交的任务扔掉
如果都不满足,那么可以自己实现策略。
提交:
submit 提交 Future 需要返回结果
execute 不需要返回结果
关闭:
shutdown()
shutdownNow()-------> 是否中断看情况(需要自己处理中断)
合理配置线程池:
maximumPoolSize
了解任务
cpu密集型 (计算) 不超过cpu的核心数,(最多+1,防止页缺失)
--->Runtime.getRuntime().availableProcessors()
io密集型 (网络,读写等) ,cpu核心数*2
混合型, 对前两种类型拆分。
相关原理参考:cpu控制IO,DMA控制器中断, OS零拷贝
android中的----->mmap 等
workQueue
尽量配置成有界的
AQS
AbstractQueuedSynchronizer
private volatile int state; 维护这个状态
同步工具类基本上extends AQS
模板设计模式
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
自定义显示锁思路:
所有显示锁实现Lock接口
拿锁和释放锁方法实现lock()和unlock()
这个时候就要借助aqs
基本思想CLH 队列锁
队列每个元素基本的结构
qNode == [mPre,locked]
如果线程A 需要获取锁,他就会把自己的aNode节点挂在等带锁的队列的尾部等待拿锁。
假设线程B 这个时也要获取锁,那么B挂在A的后面
[mPred | Locked=true qNodeA]<--------[mPred
| Locked=true qNodeB]
队列中节点的所有mPred 会自旋判断前一个节点是否释放锁,如果释放就轮到自己拿锁。自旋有一定的次数到达限制就挂起。
-> 实现自定义显示锁的代码
public class SelfLock implements Lock {
// 静态内部类,自定义同步器
private static class Sync extends AbstractQueuedSynchronizer {
/*判断处于占用状态*/
@Override
protected boolean isHeldExclusively() {
return getState()==1;
}
/*获得锁*/
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/*释放锁*/
@Override
protected boolean tryRelease(int arg) {
if(getState()==0){
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/* 返回一个Condition,每个condition都包含了一个condition队列*/
Condition newCondition() {
return new ConditionObject();
}
}
/* 仅需要将操作代理到Sync上即可*/
private final Sync sync = new Sync();
public void lock() {
System.out.println(Thread.currentThread().getName()+" ready get lock");
sync.acquire(1);
System.out.println(Thread.currentThread().getName()+" already got lock");
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public void unlock() {
System.out.println(Thread.currentThread().getName()+" ready release lock");
sync.release(1);
System.out.println(Thread.currentThread().getName()+" already released lock");
}
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
//------test
public class TestMyLock {
public void test() {
final Lock lock = new SelfLock();
class Worker extends Thread {
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName());
try {
SleepTools.second(1);
} finally {
lock.unlock();
}
}
}
// 启动4个子线程
for (int i = 0; i < 4; i++) {
Worker w = new Worker();
//w.setDaemon(true);
w.start();
}
// 主线程每隔1秒换行
for (int i = 0; i < 10; i++) {
SleepTools.second(1);
//System.out.println();
}
}
public static void main(String[] args) {
TestMyLock testMyLock = new TestMyLock();
testMyLock.test();
}
}
-> 非公平,独占,抢占方式,需要重入
/**
*类说明:实现我们自己独占锁,可重入
*/
public class ReenterSelfLock implements Lock {
/* 静态内部类,自定义同步器*/
private static class Sync extends AbstractQueuedSynchronizer {
/* 是否处于占用状态*/
protected boolean isHeldExclusively() {
return getState() > 0;
}
/* 当状态为0的时候获取锁*/
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}else if(getExclusiveOwnerThread()==Thread.currentThread()){
setState(getState()+1);
return true;
}
return false;
}
/* 释放锁,将状态设置为0*/
protected boolean tryRelease(int releases) {
if(getExclusiveOwnerThread()!=Thread.currentThread()){
throw new IllegalMonitorStateException();
}
if (getState() == 0)
throw new IllegalMonitorStateException();
setState(getState()-1);
if(getState()==0){
setExclusiveOwnerThread(null);
}
return true;
}
/* 返回一个Condition,每个condition都包含了一个condition队列*/
Condition newCondition() {
return new ConditionObject();
}
}
/* 仅需要将操作代理到Sync上即可*/
private final Sync sync = new Sync();
public void lock() {
System.out.println(Thread.currentThread().getName()+" ready get lock");
sync.acquire(1);
System.out.println(Thread.currentThread().getName()+" already got lock");
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public void unlock() {
System.out.println(Thread.currentThread().getName()+" ready release lock");
sync.release(1);
System.out.println(Thread.currentThread().getName()+" already released lock");
}
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
//------test
/**
*类说明:
*/
public class TestReenterSelfLock {
static final Lock lock = new ReenterSelfLock();
// static final Lock lock = new SelfLock();// 被死锁,没有可重新进入
public void reenter(int x){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+":递归层级:"+x);
int y = x - 1;
if (y==0) return;
else{
reenter(y);
}
} finally {
lock.unlock();
}
}
public void test() {
class Worker extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
SleepTools.second(1);
reenter(3);
}
}
// 启动3个子线程
for (int i = 0; i < 3; i++) {
Worker w = new Worker();
w.start();
}
// 主线程每隔1秒换行
for (int i = 0; i < 100; i++) {
SleepTools.second(1);
}
}
public static void main(String[] args) {
TestReenterSelfLock testMyLock = new TestReenterSelfLock();
testMyLock.test();
}
}
JMM Java 内存模型
计算机现在一般有三级缓存(还涉及:流水线和指令重排序,指令预测,条件预测等)
ARM 目前三个流水线
java 分 工作内存和主内存,工作内存是独享的。
每个线程工作内存和主内存读取数据刷新数据,会有两个问题
可见性
原子性
volitile 是强制从主内存读取数据,然后刷新回主内存,保证可见性
但是除了赋值操作,多了符合操作,但是上下文切换,回打断线程内的操作。保证不了原子性。
volitile 可以抑制重排序。
volitile 一个线程写,多个线程读是可以不用锁
但是syncronize 保证 可见性和原子性
所以有些情况 volitile + cas 操作替换 syncronize 提高性能
syncronize 底层浅析
当我们给方法加入synconize的时候,我们 用javap -v xxxx.class查看
synchronized 方法
public synchronized void incCount();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
synchronized(this) {
}
这个时候发现代码块中
发现虚拟机插入了monitor指令
monitorenter 和 monitorexit
锁的存放位置:
对象:对象头,GC年龄......MarkWord
类 类型 类指针 ...KlassPoint
对象头是随着对象运行不断放生变化
拿锁,切换两次,拿锁释放锁,一进一出。大概3到5微秒
syncronized 做了哪些优化
基于monitor指令实现,拿到同步块的执行,其他的等待
由于monitor同步比较重。所以做了一些优化
在重量级锁基础上,引入了
轻量级锁
偏向锁
偏向锁的适用场景
始终只有一个线程在执行同步块,在它没有执行完释放锁之前,没有其它线程去执行同步块,在锁无竞争的情况下使用,一旦有了竞争就升级为轻量级锁
始终只有一个线程在执行同步块,在它没有执行完释放锁之前,没有其它线程去执行同步块,在锁无竞争的情况下使用,一旦有了竞争就升级为轻量级锁,升级为轻量级锁的时候需要撤销偏向锁,撤销偏向锁的时候会导致stop the word操作;
轻量级锁 cas 等 自旋一定次数 竞争激烈膨胀后就会升级为重量级锁
锁的分类