多线程面试题1

看到不会的就查一查,然后放这
(学的很多东西都没记住,盒盒盒)

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

https://mp.weixin.qq.com/s?__biz=MzAwNDA2OTM1Ng==&mid=2453144870&idx=1&sn=9be678c536d061e0c0d10db1be58dc07&scene=21#wechat_redirect

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);
            }
        }
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值