- 线程的生命周期,线程有哪些状态
- sleep()、wait()、join()、yield()的区别
- 说说你对线程安全的理解
- 如何解决线程安全问题?有几种方法
- Thread、Runnable的区别
- 实现多线程的方法有哪些
- synchronized的概念
- synchronized与Lock的对比
- 说说你对守护线程的理解
- ThreadLocal的原理和使用场景
- ThreadLocal内存泄漏原因,如何避免
- 并行、并发、串行
- 并发的三大特性
- Java中的自增是线程安全的吗?如何实现线程安全的自增
- 为什么使用线程池?解释参数
- 简述线程池处理流程
- 线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程
- 简述volatile
- volatile和synchronized的区别
- CAS的缺点
- 线程池中线程复用原理
- 如何定义线程池参数
- 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少
- 列举出一种出现死锁的情况?java是否解决了死锁问题?
- 补充:用户态和内核态
- 补充:AQS的了解
- 补充 多线程之间的通信
- 补充 Reentranklock和Synchronized异同
线程的生命周期,线程有哪些状态
sleep()、wait()、join()、yield()的区别
上述代码执行结果22222 1111
说说你对线程安全的理解
如何解决线程安全问题?有几种方法
- synchronized的同步方法 和 同步代码块
- lock锁
Thread、Runnable的区别
拓展创建多线程的方法
上面肯定卖出10张,因为new了两个 MyThread 而ticket是类的成员变量,所以一个线程卖5个咯
实现多线程的方法有哪些
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口(比Runnable强大,可以又返回值,可以抛异常,支持范型)
- 使用线程池ExecutorService(常用参数 核心池大小、最大线程数、超时时间….)
synchronized的概念
JVM指令分析:monitorenter
互斥入口monitorexit
互斥正常出口
synchronized与Lock的对比
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
说说你对守护线程的理解
弟们 Daemon
使用java的线程池
ThreadLocal的原理和使用场景
ThreadLocal的主要作用是提供线程的局部变量,访问某个ThreadLocal变量的每个线程都有自己的局部变量,它独立于变量的初始化副本。实际上是Thread类里面维护一个ThreadLocalMap,ThreadLocalMap以该ThreadLocal对象变量为key,去取对应的value的实现过程,达到某个ThreadLocal变量的每个线程都有自己的局部值(每一线程维护变量的副本),线程间独立变化独立维护,下面会分析源码来详细介绍ThreadLocal的实现原理。
ThreadLocal内存泄漏原因,如何避免
Thread Ref->Threead->ThreadLocalMap->Entry->value这样一条强引用,而这个value永远不会被访问到了,所以存在内存泄漏
并行、并发、串行
并发的三大特性
- 原子性
不能在中间暂停然后再调度,要么都执行完,要么都不执行,整个事件是一个最小单位。
- 可见性
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他的线程狗立刻看得到修改的值
可见性就是使用计算机的两个协议:总线Lock和MESI
- 有序性
Java中的自增是线程安全的吗?如何实现线程安全的自增
- 不安全 见上题 实际上是4个步骤
- 考虑原子性,4个要么都执行,要么都不执行
为什么使用线程池?解释参数
简述线程池处理流程
线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程
简述volatile
volatile和synchronized的区别
volatile | synchronized | |
---|---|---|
使用上 | 只能修饰变量 | 方法和代码块 |
对原子性的保证 | 不能保证原子性 | 可以保证原子性 |
对可见性对保证 | 可以,对变量加了lock | 可以,使用monitorEnter和monitorExit |
对有序性对保证 | 可以(禁止指令重排) | 可以但代价太大,重量级,并发退化串行 |
其他 | 不会引起阻塞 | 引起阻塞 |
CAS的缺点
线程池中线程复用原理
如何定义线程池参数
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少
package com.cheung.JUI;
public class test1 {
private int i;
private synchronized void inc(){
i++;
System.out.println(Thread.currentThread().getName() + "--inc--" + i);
}
private synchronized void dec(){
i--;
System.out.println(Thread.currentThread().getName() + "--dec--" + i);
}
//内部类
class Inc implements Runnable{
@Override
public void run() {
inc();
}
}
//内部类
class Dec implements Runnable{
@Override
public void run() {
dec();
}
}
public static void main(String[] args) {
test1 t1 = new test1();
//内部类的实例化
Inc inc = t1.new Inc();
Dec dec = t1.new Dec();
for(int i = 0; i < 2; i++){
new Thread(inc).start();
new Thread(dec).start();
}
}
}
列举出一种出现死锁的情况?java是否解决了死锁问题?
银行两个账户转账问题。(同时锁两个,他们两个还不能死锁) 对id排序
补充:用户态和内核态
概念:
- 内核态 CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序
- 用户态 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取
为什么要有用户态和内核态:
- 由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级
用户态切换到内核态三种方式:
- 系统调用
- 异常
- 外部设备的中断
java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。
synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋锁,他们都属于乐观锁。所以明确java线程切换的代价,是理解java中各种锁的优缺点的基础之一。
补充:AQS的了解
https://blog.csdn.net/oldshaui/article/details/102692646?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161637387816780274131548%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161637387816780274131548&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-1-102692646.first_rank_v2_pc_rank_v29&utm_term=aqs
AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。
补充 多线程之间的通信
补充 Reentranklock和Synchronized异同
https://blog.csdn.net/qq_40551367/article/details/89414446?utm_source=app&app_version=4.5.3
同:
- 都是加锁方式同步
- 都是重入锁
- 都是阻塞式的同步(用户态-内核态)
不同:
- 这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。(底层minitor enter/exit)
- 而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。(底层CAS+CLH队列)实现的前提是AQS
- 需要手动释放,不需要手动释放
- Syn不可中断,而Reentranklock等待线程可以放弃等待。