(4/100)

学习思路如下:

面试准备思路

一、JavaSE部分—题目

5、线程
1、多线程中的i++线程安全吗?为什么?
2、如何线程安全的实现一个计数器?
3、多线程同步的方法
4、介绍一下生产者消费者模式?
5、线程,进程,然后线程创建有很大开销,怎么优化?
6、线程池运行流程,参数,策略
7、讲一下AQS吧。
8、创建线程的方法,哪个更好,为什么?
9、Java中有几种方式启动一个线程?
10、Java中有几种线程池?
11、线程池有什么好处?
12、cyclicbarrier和countdownlatch的区别
13、如何理解Java多线程回调方法?
14、创建线程有几种不同的方式?你喜欢哪一种?为什么?
15、概括的解释下线程的几种可用状态。
16、同步方法和同步代码块的区别是什么?
17、启动线程有哪几种方式,线程池有哪几种?
18、在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
19、sleep() 和 wait() 有什么区别?
20、同步和异步有何异同,在什么情况下分别使用他们?举例说明。
21、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。使用内部类实现线程,对j增减的时候没有考虑顺序问题。
22、启动一个线程是用run()还是start()?
23、请说出你所知道的线程同步的方法
24、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
25、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
26、线程的sleep()方法和yield()方法有什么区别?
27、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
28、请说出与线程同步以及线程调度相关的方法。
29、举例说明同步和异步
30、什么是线程池(thread pool)?
31、说说线程的基本状态以及状态之间的关系?
32、如何保证线程安全?

答案部分:5)线程

参考—https://blog.csdn.net/asc_123456/article/details/90173511

1、多线程中的i++线程安全吗?为什么?

答:不安全。i++不是原子性操作。i++分为读取i值,对i值加一,再赋值给i++,执行期中任何一步都是有可能被其他线程抢占的。

总结: 每个线程都有自己的工作内存,每个线程需要对共享变量操作时必须把共享变量从主内存中加载到自己的工作内存。等完成操作再保存到内存中。如果一个线程运算完成后还没刷到主内存中,另一个线程又对这个共享变量进行操作,那么读取到的数据就是脏数据了。

2、如何线程安全的实现一个计数器?

答:链接—https://www.jianshu.com/p/785494999905
思路:
* 使用synchronized关键字修饰计数器方法
* 并且使用AtomicInteger类线程安全的一种原子操作的Integer类进行操作
使用AtomicInteger类来作为变量,它保证了数据的原子性。并且加锁。

3、多线程同步的方法 ?

答:

  • java中多线程的实现方法有两种:
    1.直接继承thread类;
    2.实现runnable接口;
  • 同步的实现方法有五种:
    1.同步方法;
    2.同步代码块;
    3.使用特殊域变量(volatile)实现线程同步;
    4.使用重入锁实现线程同步;
    5.使用局部变量实现线程同步 。
    其中多线程实现过程中需注意重写或覆盖run()方法,对于同步的实现方法中使用较常使用的是利用synchronized编写同步方法和代码块。

4、介绍一下生产者消费者模式?

答:作用:解耦、支持并发、支持忙闲不均。

生产者消费者模式:通过一个容器来解决生产者和消费者的强耦合关系,生产者生成数据无需等待消费者索取,消费者无需直接索要数据。两者并不进行任何通讯,而是通过容器来进行操作

5、线程,进程,且线程创建有很大开销,怎么优化?

答:使用线程池。
线程池维护了多个线程,当我们的程序需要用到线程时,就直接从其中获取,不用的是时候放回池中。使用线程池可以减低资源的消耗。可以提高响应的速度。可以方便线程的管理。可以提高线程的使用率。

6、线程池运行流程,参数,策略?

答:

  • 运行流程:
    (1) 线程池先判断池中是否有没执行任务的线程,如果有,则创建出来一个来使用(这里指的是corePollSize)
    (2) 如果线程池中的线程都在执行任务。将任务放在一个队列中等待分配线程。
    (3) 如果这个队列也满,则在线程池中创建一个线程来给新的任务。如果运行的线程大于我们设置的最大线程数量(maximumPollSize)时,就会把任务交给饱和策略(默认是抛出异常)来处理。

  • 参数:
    (1) corePollSize:核心线程数。在创建线程池之后,池中还没有线程,当新的任务来的时候就会在池中创建,当创建的线程数达到corePollSize的数量的时候,就会吧任务放进一个队列中等待。
    (2) maximumPollSize:最大线程数.
    (3) keepAliveTime:空闲线程保留的时间。
    (4) TimeUnit:空闲时间保留的时间单位。
    TimeUnit.DAYS 天。TimeUnit.HOURS 小时。等等
    (5) BlockingQueue<Runnable’>:队列
    (6) ThreadFactory:线程工厂
    (7) RejectedExecutionHandler:策略

  • 策略:饱和策略
    new ThreadPoolExecutor
    (corePoolSize,maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue,handler);最后的参数handler,就是饱和策略。有4种:
    (1) AbortPolicy:直接抛出异常、
    (2) CallerRunsPolicy:只用调用者所在线程来运行任务、
    (3) DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务、 DiscardPolicy:不处理,丢弃掉

7、讲一下AQS吧

答:AQS: Abstract Queued Synchronizer,是java并发包中的核心类,诸如ReentrantLock,CountDownLatch等工具内部都使用了AQS去维护锁的获取与释放。AQS是一个并发包的基础组件,它包含了state变量,加锁线程,等待队列并发中的核心组件,用来实现各种锁,各种同步组件。

8、创建线程的方法,哪个更好,为什么?

答:参考—https://www.cnblogs.com/zwwhnly/p/11890032.html
1)继承Thread类
2)实现Runnable接口(推荐)
3)实现Callable接口

  • 区别:
    (1)Java中,类仅支持单继承,如果一个类继承了Thread类,就无法再继承其它类,因此,如果一个类既要继承其它的类,又必须创建为一个线程,就可以使用实现Runable接口的方式。
    (2)使用实现Runable接口的方式创建的线程可以处理同一资源,实现资源的共享。
    (3)使用实现Callable接口的方式创建的线程,可以获取到线程执行的返回值、是否执行完成等信息。

总结:
一共有3中创建线程的方式1.继承Thread类,重写run()方法,2.实现Runnable接口3.使用线程池

使用Runnable更好,因为实现了Runnable,还需要用Thread类进行包装,才能使用start方法。而对象的包装实现了资源的共享,一个类可以实现多个接口,避免单继承带来的局限。

9、Java中有几种方式启动一个线程?

答:

1)继承Thread类,重写它的run类,然后实例化调用start()方法。

2)实现Runnable接口,实现run方法,使用Thread包装该类,实例化、调用start()方法。

3)实现Callable接口,重写call()方法,然后实例化调用start()方法。

4)使用线程池,使用execute把任务添加到其中。

10、Java中有几种线程池?

答:有6大线程池。参考—https://blog.csdn.net/qq_26963495/article/details/79056391
(1)FixedThreadPool:
特点:固定池子中线程的个数。使用静态方法newFixedThreadPool()创建线程池的时候指定线程池个数。

(2)CachedThreadPool(弹性缓存线程池):
特点:用newCachedThreadPool()方法创建该线程池对象,创建之初里面一个线程都没有,当execute方法或submit方法向线程池提交任务时,会自动新建线程;如果线程池中有空余线程,则不会新建;这种线程池一般最多情况可以容纳几万个线程,里面的线程空余60s会被回收。

(3)SingleThreadPool(单线程线程池):
特点:池中只有一个线程,如果扔5个任务进来,那么有4个任务将排队;作用是保证任务的顺序执行。

(4)ScheduledThreadpool(定时器线程池)
(5)WorkStealingPool
(6)ForkJoinPool

11、线程池有什么好处?

答:参考—https://www.cnblogs.com/WangJinYang/p/10226866.html
1)线程使应用能够更加充分合理的协调利用cpu 、内存、网络、i/o等系统资源。

3)线程的创建需要开辟虚拟机栈,本地方法栈、程序计数器等线程私有的内存空间。

3)在线程的销毁时需要回收这些系统资源。频繁的创建和销毁线程会浪费大量的系统资源,增加并发编程的风险。

4)在服务器负载过大的时候,如何让新的线程等待或者友好的拒绝服务?这些丢失线程自身无法解决的。所以需要通过线程池协调多个线程,并实现类似主次线程隔离、定时执行、周期执行等任务。

  • 线程池的作用包括:
    (1)利用线程池管理并复用线程、控制最大并发数等。
    (2)实现任务线程队列缓存策略和拒绝机制。
    (3)实现某些与时间相关的功能,如定时执行、周期执行等。
    (4)隔离线程环境。比如,交易服务和搜索服务在同一台服务器上,分别开启两个线程池,交易线程的资源消耗明显要大;因此,通过配置独立的线程池,将较慢的交易服务与搜索服务隔开,避免个服务线程互相影响。

12、cyclicbarrier和countdownlatch的区别

答:阻塞线程。参考—https://www.cnblogs.com/twoheads/p/9555867.htmlCountdownLatch
1) CountdownLatch阻塞主线程,等所有子线程完结了再继续下去。
2) Syslicbarrier阻塞一组线程,直至某个状态之后再全部同时执行,且所有线程都被释放后,还能通过reset来重用。

13、如何理解Java多线程回调方法?

答:

  • 回调理解: Java多线程回调,指的是调用者执行调用时将自己作为参数给被调用者,被调用者达到一定条件后通过这个参数调用调用者,多线程下回调多用于同时计算运算的结果回调给调用者。
  • 回调举例: 客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说 这个B便叫做回调方法。

14、创建线程有几种不同的方式?你喜欢哪一种?为什么?

答:参考—https://blog.csdn.net/qq_40574571/article/details/90766161
1)继承Thread类,重写run方法;
2)实现Runnable接口,重写run方法,比继承Thread类好用,实现接口还可以继承类,避免了单继承带来的局限性;
3)使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。

  • 一般情况下,常见的是第二种。
    Runnable接口有如下好处:
    ①避免点继承的局限,一个类可以继承多个接口。
    ②适合于资源的共享

15、概括的解释下线程的几种可用状态。

答:如图所示
线程状态

  • 1)新建(new): 新建一个线程对象。
  • 2)可运行状态(runnable): 线程对象创建后,其他线程调用该对象的start()方法,该状态的线程位于可运行线程池中,等待线程调度选中,获取CPU使用权。
  • 3)运行状态(running): 可运行状态的线程获取到了cpu时间片(timeslice),执行程序代码。
  • 4)阻塞(block): 运行状态的线程因为某些原因放弃了CPU的使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行状态,才有机会再次获得cpu timeslice转到运行状态。阻塞的情况分为三种:
    (a)等待阻塞:运行状态的线程对象执行了wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (b)同步阻塞:运行状态的线程对象要获取同步锁时,若该同步锁被别的线程占用,则JVM就会把该线程放入锁池(lock pool)中。
    (c)其他阻塞:运行状态的线程对象调用了sleep()、join()方法或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、I/O处理完毕时,线程转入可运行状态。
  • 5)死亡(dead): 线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可复生。

16、同步方法和同步代码块的区别是什么?

答:

  • 同步方法: 是有 synchronize修饰的方法,他的锁就是当前类对象。
  • 同步代码块: 是有synchronize修饰的代码块,默认为当前类对象,但是也可以放其他的类对象。

17、启动线程有哪几种方式,线程池有哪几种?

答:
要启动的可以分为两类:返回结果和不返回结果。对于这两种,也分别有两种启动线程的方式:
1)继承Thread类,重载run()
2)实现Runnable接口,实现run()
3)实现Callable接口,通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的线程

  • 常用线程池:
    1)newCachedThreadPool():不限数目的线程池,完全依赖于JVM能创建的线程数,可能出现内存不足
    2)newFixedThreadPool(int nThreads):指定数目的线程池,如果多于这个数目则加入缓存队列
    3)newSingleThreadExecutor():单线程的线程池,处理完一个任务接着下一个,若异常则起一个新的线程
    4)newScheduleThreadPool()
    5)newWorkStealingPool(int parallelism):通过修改五大核心参数来控制;
    线程池五大参数

18、在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

答:

  • (1)监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。

  • 1) 在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁。
    2) 一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域, 确保一次只能有一个线程执行该部分的代码, 线程在获取锁之前不允许执行该部分的代码。
    3) 另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案。

19、sleep() 和 wait() 有什么区别?

答:都用来进行线程控制,他们最大本质的区别是:sleep()不释放同步锁,wait()释放同步锁;

  • sleep和wait的区别有:
    1)这两个方法来自不同的类分别是Thread(自控)和Object(他控)
    2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
    3)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
    synchronized(x){
    x.notify()
    //或者wait()}
    4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

20、同步和异步有何异同,在什么情况下分别使用他们?举例说明。

答:

  • 同步: 如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。

  • 异步: 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

Java中交互方式分为同步和异步两种:

  • 同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;

  • 异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。

  • 区别: 一个需要等待,一个不需要等待,在部分情况下,项目开发中都会优先选择不需要等待的异步交互方式。

优先同步的交互:比如银行的转账系统,对数据库的保存操作等等,其余情况都优先使用异步交互。

21、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。使用内部类实现线程,对j增减的时候没有考虑顺序问题。

答:参考—https://www.cnblogs.com/nannanITeye/p/3421943.html

package mystudy;

public class ManyThreads {

    private int j;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ManyThreads many = new ManyThreads();
        Inc inc = many.new Inc();
        Dec dec = many.new Dec();
        for (int i = 0; i < 2; i++) {
            Thread t = new Thread(inc);
            t.start();
            t = new Thread(dec);
            t.start();
        }
    }

    private synchronized void inc() {
        j++;
        System.out.println(Thread.currentThread().getName() + "inc" + j);
    }

    private synchronized void dec() {
        j--;
        System.out.println(Thread.currentThread().getName() + "dec" + j);
    }

    class Inc implements Runnable {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            for (int i = 0; i < 20; i++) {
                inc();
            }
        }
    }

    class Dec implements Runnable {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            for (int i = 0; i < 20; i++) {
                dec();
            }
        }
    }
}

.

22、启动一个线程是用run()还是start()?

答:启动线程要用start()方法。参考—https://www.cnblogs.com/116970u/p/11508812.html

  • 当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。start()是方法,它调用run()方法。而run()方法是你必须重写的. run()方法中包含的是线程的主体。

23、请说出你所知道的线程同步的方法

答:

1)同步方法
2)同步块
3)wait 和 notify
4)volatile
5)Lock:ReentrantLock
6)局部变量:比如ThreadLocal
7)blockqueue

24、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

答:参考—https://www.cnblogs.com/liujichang/p/3150387.html

  • 1)多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
  • 2)同步的实现方面有两种,分别是synchronized,wait与notify

25、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

答:

  • (1) 有两种实现方法,分别是继承Thread类与实现Runnable接口
  • (2) 用synchronized关键字修饰同步方法
  • 1) stop()线程不安全。 它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
  • 2) suspend()方法容易发生死锁。 调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

26、线程的sleep()方法和yield()方法有什么区别?

答:
1) sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会

2) yield()方法只会给相同优先级或更高优先级的线程以运行的机会

3) 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态

4) sleep()方法声明会抛出InterruptedException,而yield()方法没有声明任何异常

5) sleep()方法比yield()方法具有更好的移植性(跟操作系统CPU调度相关)

27、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

答:是不能的,其他线程只能访问该对象的非同步方法,同步方法则不能进入;

  • 因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法,说明对象锁已经被取走了,那么试图进入B方法的线程就只能在等锁池(注意这里不是等待池)中等待对象的锁。

  • 如果不是synchronized则分两种情况

       1):进入此对象的非同步方法
    
            答案:可以
    
       2):进入此对象的同步方法
    
           答案:不可以
    

28、请说出与线程同步以及线程调度相关的方法。

答:

  • wait(): 使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
  • sleep(): 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
  • notify(): 唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  • notityAll(): 唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

29、举例说明同步和异步

答:同步和异步最大的区别就在于。一个需要等待,一个不需要等待

  • 同步: 发送一个请求,等待返回,然后再发送下一个请求
  • 异步: 发送一个请求,不等待返回,随时可以再发送下一个请求

同步: 可以避免出现死锁,读脏数据的发生,一般共享某一资源的时候用,如果每个人都有修改权限,同时修改一个文件,有可能使一个人读取另一个人已经删除的内容,就会出错,同步就会按顺序来修改。

异步: 可以提高效率了,现在cpu的双核,四核,异步处理的话可以同时做多项工作,但必须保证是可以并发处理的。

异步举例: 短信。发起者不关心接收者的状态,不需要等待接收者的返回信息,则可以进行下一次发送。
同步举例: 电话。发起者需要等待接收者,接通电话后,通信才开始,需要等待接收者的返回信息。

30、什么是线程池(thread pool)?

答:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。

  • 在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

31、说说线程的基本状态以及状态之间的关系

答:如图所示

  • 线程有五个状态:
  • New创建状态,Runnable就绪状态,Running运行状态,Dead消亡状态,Blocked阻塞状态。
  • 创建线程通过start方法进入就绪状态,获取cpu的执行权进入运行状态,失去cpu执行权会回到就绪状态,运行状态完成进入消亡状态,运行状态通过sleep方法和wait方法进入阻塞状态,休眠结束或者通过notify方法或者notifyAll方法释放锁进入就绪状态。
    线程图解

32、如何保证线程安全?

答:参考—https://blog.csdn.net/weixin_40459875/article/details/80290875

  • 1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(atomic,synchronized);

  • 2.可见性:一个线程对主内存的修改可以及时地被其他线程看到(synchronized,volatile);

  • 3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则)。

第4天(1/1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值