线程状态

初始状态(NEW)

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

运行状态(RUNNABLE)

Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)

阻塞状态(BLOCKED)

当获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间

当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态

等待(WAITING)

处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

超时等待(TIMED_WAITING)

当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间

当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁

如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁

还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时时间到自然恢复为可运行状态

终止状态(TERMINATED)

当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。



线程池的核心参数

核心线程:当线程执行完任务以后,仍然需要保留在线程中

救急线程:当线程执行完任务以后,不需要保留在线程中

  1. corePoolSize:核心线程数目(池中会保留的最多线程数)
  2. maximumPoolSize:最大线程数目(核心线程+救急线程的最大数目)
  3. keepAliveTime:生存时间(救急线程的生存时间,生存时间内没有新任务,此线程资源会释放)
  4. unit:时间单位(救急线程的生存时间单位,如秒、毫秒等)
  5. workQueue:当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
  6. threadFactory:线程工厂(可以定制线程对象的创建,例如设置线程名字、是否是守护线程等)
  7. handler:拒绝策略(当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略))

wait和sleep

共同点

  • wait() ,wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态

不同点

  • 方法归属不同

    • sleep(long) 是 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 的实现通常会提供更好的性能


公平锁与非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁

优点:所有的线程都能得到资源,不会饿死在队列中
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,而cpu唤醒阻塞线程的开销大

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁

优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死

可以通过对构造器传值来决定使用哪种锁

image-20220924183450145
1
ReentrantLock lock = new ReentrantLock(true);

注意:

tryLock带参数的方法,会根据ReentrantLock设置的true或false实现公平或非公平锁

tryLock无参的方法,则是默认使用了非公平锁


RentrantLook

Sync类是ReentrantLock他本身的一个内部类,它继承了AbstractQueuedSynchronizer,在操作锁的大部分操作,都是Sync本身去实现的

image-20220924182512166

Sync分别有两个子类:FairSync和NofairSync,分别体现了公平锁与非公平锁

image-20220924182634198

公平锁的实现与否主要是这个hasQueuedThreads方法

判断当前的线程是不是位于同步队列的首位,是就是返回true,否就返回false

image-20220924182722113 image-20220924182812496

volatile

线程安全要考虑三个方面:可见性、有序性、原子性

可见性:一个线程对共享变量修改,另一个线程能看到最新的结果

有序性:一个线程内代码按编写顺序执行

原子性:一个线程内多行代码以一个整体运行,期间不能有其他线程的代码插队

volatile只能保证可见性和有序性,不能保证原子性

解决可见性问题

起因:由于编译器优化、或缓存优化、或 CPU 指令重排序优化导致的对共享变量所做的修改另外的线程看不到

解决:用 volatile 修饰共享变量,能够防止编译器等优化发生,让一个线程对共享变量的修改对另一个线程可见

解决有序性问题

起因:由于编译器优化、或缓存优化、或 CPU 指令重排序优化导致指令的实际执行顺序与编写顺序不一致

解决:使用volatile修饰变量,会在读或写时候加入不同的屏障,避免读或者写的操作越过屏障,从而达到阻止重排序的效果

image-20220924191509947

原子性问题

起因:多线程下,不同线程的指令发生了交错导致的共享变量的读写混乱

解决:用悲观锁或乐观锁解决,volatile 并不能解决原子性


悲观锁

悲观锁的代表是 synchronized 和 Lock 锁

核心思想:线程只有占有了锁,才能去操作共享变量,每次只有一个线程占锁成功。任何获取锁失败的线程,都得停下来等待

缺点:线程从运行到阻塞,从阻塞到唤醒,涉及线程上下文切换,如果频繁发生,影响性能

实际上,线程在获取synchronized 和 Lock 锁时,如果锁已经被占用,都会做几次重试操作,减少阻塞的机会


乐观锁

乐观锁的代表是 AtomicInteger,使用CAS来保证原子性

核心思想:无需加锁,每次只有一个线程能成功修改共享变量,其他失败的线程不需要停止,不断重试直到成功

缺点:需要多核CPU支持,且线程数不应超过CPU核数

  • hashmap和hashtable区别
  • 为什么产生死锁
  • jvm类加载
  • java反射获取私有属性,改变值
  • 反射用途
  • JVM内存模型
  • 垃圾回收机制
  • 项目中查看垃圾回收
  • ConcurrentHashMap底层原理
  • HashMap底层数据结构
  • JDK1.8中的HashMap为什么用红黑树不用普通的AVL树?
  • 为什么在JDK8的时候链表变成树?
  • 线程池7个参数,该怎么配置最好?
  • 说一下volatile
  • volatile的可见性和禁止指令重排序怎么实现的?
  • CAS是什么
  • PriorityQueue底层是什么,初始容量是多少,扩容方式呢?
  • HashMap的容量为什么要设置为2的次幂?
  • CopyOnWriteArrayList知道吗,迭代器支持fail-fast吗?
  • synchronized关键字的用法
  • synchronized修饰类方法和普通方法的锁区别,获取类锁之后还能获取对象锁吗
  • 类加载器的双亲委派模型的作用,能重复加载某个类吗
  • 类加载器的类的缓存,key是什么
  • 字节码结构
  • HashMap在大量哈希冲突该怎么处理
  • HashMap查找效率
  • 讲一下线程池,以及实现固定大小线程池底层是如何实现的?
  • 堆内存和栈内存有什么区别
  • ThreadLocal的使用场景
  • hashmap结构;什么对象能做为key
  • hashtable,concurrentHashMap,hashtable比较
  • wait,sleep分别是谁的方法,区别
  • countLatch的await方法是否安全,怎么改造
  • 线程池参数,整个流程描述 ,背后的底层原理aqs,cas
  • 对Runtime的了解
  • 什么情况会造成内存泄漏
  • 新生代分为几个区?使用什么算法进行垃圾回收?为什么使用这个算法?
  • 如何解决同时存在的对象创建和对象回收问题
  • 什么是活锁、饥饿、无锁、死锁?怎么检测一个线程是否拥有锁?
  • 为什么 Java 要采用垃圾回收机制,而不采用 C/C++的显式内存管理?
  • 一个线程的生命周期有哪几种状态?它们之间如何流转的?
  • Java容器有哪些?哪些是同步容器,哪些是并发容器?
  • 讲一讲AtomicInteger,为什么要用CAS而不是synchronized?
  • java中的线程有几种状态?详细说明
  • Java 初始化一个线程池有哪些参数可以配置, 分别是什么作用?
  • 什么对象会从新生代晋升到老年代
  • HashMap ConcurrentHashMap的区别?
  • (Java)下面聊一下java,集合用过哪些,源码看过哪些
  • (Java)hashmap详细说一下
  • (Java)hashmap的扩容为什么是2倍(告诉我和hash算法有关)
  • (Java)ConcurrentHashMap说一下,怎么实现线程安全的,分段锁是怎么实现的(没回答出来,面试官告诉我AQS)
  • (Java)说一下AQS
  • (Java)synchronized说一下,我说了使用场景,实现原理,优化
  • (Java)volatile说一下
  • (Jvm)JVM内存结构说一下,堆栈方法区这些,我问了面试官一个问题,为什么叫方法区,面试官说没有思考过
  • (Jvm)J说一下堆和栈
  • (Jvm)J类加载机制
  • (Jvm)J垃圾回收原理,原理,算法,cms和G1的区别
  • (Jvm)JStackOverFlow有遇到过吗?说一下
  • (Jvm)JOOM有遇到过吗,说一下,怎么分析OOM
  • (Java)什么是面向对象,结合面向过程进行了对比
  • (Java)StringBuffer和StringBuilder的区别说一下
  • (Java)ArrayList和LinkList的区别说一下,插入的时间复杂度
  • CAS原理说一下?
  • 多线程都有哪些锁?
  • synchronized和lock区别?
  • jvm内存模型
  • GC机制
  • 类加载机制
  • 双亲委派模型
  • (Java)Object用到了哪些方法
  • (Java)Object线程同步的相关方法
  • (Java)说说对线程的理解
  • (Java)java中如何创建线程
  • (Java)java如何实现线程的
  • (Java)synchronized和volatile区别,二者的实现原理
  • (Java)synchronized修饰静态变量和非静态变量的区别
  • jdk的动态代理是怎么实现的
  • (Java)说说类加载器,双亲委派了解吗?
  • (Java) 说说对JVM的了解
  • (Java) 为什么说Java是跨平台的?
  • (Java) JVM的组成?每一块的作用?
  • (Java) 垃圾回收GC分代和GC算法
  • (Java) 什么样的对象会在堆里?
  • (Java) ReentrantLock的特点?
  • (Java) 公平锁非公平锁?
  • (Java) AQS的实现原理,包括其中的cas,cas是怎么实现的,有什么好处?
  • (Java) Hashmap的实现原理?
  • (Java) 线程安全的问题
  • (Java)hashmap是怎么实现的
  • (Java)ConcurrentHashMap和Hashmap的区别
  • (Java)hashmap冲突怎么办
  • (Java)java的深拷贝和浅拷贝
  • hashmap 和 hashtable 的区别说一下
  • hashtable 怎么保证线程安全的?
  • synchronized 是怎么实现的,监视器锁是怎么实现的
  • ConcurrentHashMap 是怎么实现的?CAS 的问题是什么。CAS适用哪种场景。
  • 如何解决 hash 的碰撞问题
  • hashmap 的扩容说一下,怎么实现的。
  • hashtable 的扩容是线程安全的吗