看到不会的就查一查,然后放这
(学的很多东西都没记住,盒盒盒)
0.线程池相关
这块我完全没会,【占坑】
1.
问题:
Java中线程与操作系统的线程关系
答案:
由上面源码可以看到:Java的多线程是直接调用linux的多线程函数实现的
每个继承java.lang.Thread的类,调用start方法之后,都调用start0()的native方法;
start0()的native方法在openjdk里调用的是JVM_StartThread;
JVM_StartThread最终调用的是操作系统的pthread_create()函数,有四个参数,我们写的run方法就是该函数的第三个参数.
参考:https://www.cnblogs.com/lusaisai/p/12729334.html
2.
问题:
spring怎么保证线程安全?
答案:
3.
问题
happens-before原则
答案
4.
问题
JMM
答案
JMM本身是一组抽象的概念并不存在,它描述的是一组规则或规范,通过这组规范定义了程序中中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
JMM的三大特性:原子性、可见性、隔离性。
JMM关于同步的规定:
1、线程解锁前,必须把共享变量的值刷新回主内存;
2、线程加锁前,必须读取主内存的最新值到自己的工作内存;
3、加锁解锁是同一把锁;
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有的变量都存储在主内存,主内存是共享内存区域,所有的线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后在将变量写回主内存,不能直接操作主内存中的变量,各个线程的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信必须通过主内存来完成。
举例如图:如果A线程把X修改成1之后,A线程会把X从A的本地内存中写入到主内存中,这样的话主内存的X就等于1了,这时候B线程就会去读取主内存的X变量,存入B的本地内存中,这样B线程的X变量值也就会变成了1。
5.
问题
Runnable和Callable的区别
答案
1)Runnable提供run方法,无法通过throws抛出异常,所有CheckedException必须在run方法内部处理。Callable提供call方法,直接抛出Exception异常。
2)Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值。
public interface Callable<V> {
V call() throws Exception;
}
3)Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而Callable只能通过线程池执行。
6.
问题
sleep和wait的区别和共同点
答案
7.
问题
synchronized在JDK6之后做了哪些优化
答案
这里的答案不是很具体
8.ThreadLocal
ThreadLocal的作用主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,防止自己的变量被其它线程篡改。
每个线程Thread都维护了自己的threadLocals变量,所以在每个线程创建ThreadLocal的时候,实际上数据是存在自己线程Thread的threadLocals变量里面的。
9.死锁是什么,怎么解决
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
java 死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
10.sleep出现interrrupt
通过interrupt方法打断sleep。这时会出一个InterruptedException异常.
public class Main {
public static void main(String[] args){
Thread t1=new Thread(){
public void run() {
System.out.println("线程启动了");
try {
Thread.sleep(1000 * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程结束了");
}
};
t1.start();//启动t1
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
}
t1.interrupt();//打断t1
}
}
11.两个线程输入xyyx
public class PrintXY {
static int x = 0;
static int y = 0;
static Object obj = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
while (x < 2) {
while (y < 2 && x != 0) {
//第二次打印x:x打印过一次时,需要y=2再打印x,
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("x");
x++;
obj.notify();
}
}
}
});
t1.setName("Therad1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
while (y < 2) {
while (x == 0) {//需要等x变为1才打印yy
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("y");
y++;
obj.notify();
}
}
}
});
t2.setName("Therad2");
t1.start();
t2.start();
}
}
12.
问题:什么时候用多线程
答案:
一、高并发
二、线程后台处理大任务
三、大任务
大任务处理起来比较耗时,这时候可以起到多个线程并行加快处理(例如:分片上传)。
13.
问题
volatile修饰一个对象,这个对象里的属性具备可见性吗?
答案
被volatile关键字修饰的对象作为类变量或实例变量时,其对象中携带的类变量和实例变量也相当于被volatile关键字修饰了
https://zhuanlan.zhihu.com/p/101810879
问题
volatile修饰的是地址,还是修饰的空间
答案
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
14.
问题
提交到线程池的任务报错了,主线程怎么捕获到这个错误 FutureTask
答案
afterExecute方法是捕获FutureTask抛出的异常。submit方法提交的任务可以使用这个方法捕获异常。
public class AfterExecuteException {
public static void main(String[] args) {
int corePoolSize = 3;
int maxPoolSize = 5;
long keepAliveTime = 10;
BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(5);
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime,
TimeUnit.SECONDS, queue) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (r instanceof FutureTask<?>) {
try {
((FutureTask<?>) r).get();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
System.out.println("afterExecute 捕获到线程的异常返回值" + e.getMessage());
}
}
}
};
runnable(executor);
}
static void runnable(ThreadPoolExecutor executor) {
TestRunnable testRunnable = new TestRunnable();
for (int i = 0; i < 5; i++) {
executor.submit(testRunnable); //submit方法提交
// executor.execute(testRunnable); //execute方法提交异常只会直接抛出异常
}
executor.shutdown();
}
static class TestRunnable implements Runnable {
private static volatile int i = 0;
@Override
public void run() {
i++;
if (i == 2) {
throw new RuntimeException("子线程异常,当前 i 的 值:" + i);
} else {
System.out.println(Thread.currentThread().getName() + " 子线程执行--->>> i 的值:" + i);
}
}
}
}