• 线程(二)


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

    考察点:JAVA线程

    参考回答:

    有三种方式可以用来创建线程: 
    继承Thread类 
    实现Runnable接口 
    应用程序可以使用Executor框架来创建线程池 
    实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。

    ● 请解释一下Java多线程回调是什么意思?

    考察点:JAVA多线程

    参考回答:

    所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。

    ● 请列举一下启动线程有哪几种方式,之后再说明一下线程池的种类都有哪些?

    考察点:线程池

    参考回答:

    ①启动线程有如下三种方式:

    一、继承Thread类创建线程类

    (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

    (2)创建Thread子类的实例,即创建了线程对象。

    (3)调用线程对象的start()方法来启动该线程。

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.thread;
    public class FirstThreadTest extends Thread{
        int i = 0;
        //重写run方法,run方法的方法体就是现场执行体
        public void run()
        {
            for(;i<100;i++){
            System.out.println(getName()+"  "+i);
             
            }
        }
        public static void main(String[] args)
        {
            for(int i = 0;i< 100;i++)
            {
                System.out.println(Thread.currentThread().getName()+"  : "+i);
                if(i==20)
                {
                    new FirstThreadTest().start();
                    new FirstThreadTest().start();
                }
            }
        }
      
    }

    上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。GetName()方法返回调用该方法的线程的名字。

    二、通过Runnable接口创建线程类

    (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

    (2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

    (3)调用线程对象的start()方法来启动该线程。

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.thread;
      
    public class RunnableThreadTest implements Runnable
    {
      
        private int i;
        public void run()
        {
            for(i = 0;i <100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" "+i);
            }
        }
        public static void main(String[] args)
        {
            for(int i = 0;i < 100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==20)
                {
                    RunnableThreadTest rtt = new RunnableThreadTest();
                    new Thread(rtt,"新线程1").start();
                    new Thread(rtt,"新线程2").start();
                }
            }
      
        }
      
    }

    三、通过Callable和Future创建线程

    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

    (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

    (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package com.thread;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
      
    public class CallableThreadTest implements Callable<Integer>
    {
      
        public static void main(String[] args)
        {
            CallableThreadTest ctt = new CallableThreadTest();
            FutureTask<Integer> ft = new FutureTask<>(ctt);
            for(int i = 0;i < 100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
                if(i==20)
                {
                    new Thread(ft,"有返回值的线程").start();
                }
            }
            try
            {
                System.out.println("子线程的返回值:"+ft.get());
            catch (InterruptedException e)
            {
                e.printStackTrace();
            catch (ExecutionException e)
            {
                e.printStackTrace();
            }
      
        }
      
        @Override
        public Integer call() throws Exception
        {
            int i = 0;
            for(;i<100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" "+i);
            }
            return i;
        }
      
    }

    ②线程池的种类有:

    Java通过Executors提供四种线程池,分别为:
    newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    ● 请简要说明一下JAVA中cyclicbarrier和countdownlatch的区别分别是什么?

    考察点:线程

    参考回答:

    CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

    而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

    另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

    ● 请说明一下线程池有什么优势?

    考察点:线程池

    参考回答:

    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。

    第三:提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

    ● 请回答一下Java中有几种线程池?并且详细描述一下线程池的实现过程

    考察点:线程池

    参考回答:

    1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。 
    2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是: 
    1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。 
    2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。 
    3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。 
    4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻) 

    ● 请说明一下Java中都有哪些方式可以启动一个线程?

    考察点:线程

    参考回答:

    1. 继承自Thread类

    2. 实现Runnable接口

    3.即实现Runnable接口,也继承Thread类,并重写run方法

    ● 请列举一下创建线程的方法,并简要说明一下在这些方法中哪个方法更好,原因是什么?

    考察点:线程

    参考回答:

    需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;

    实现Runnalbe接口,重载Runnalbe接口中的run()方法。

    实现Runnalbe接口更好,使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享.

    ● 请简短说明一下你对AQS的理解。

    考察点:多线程

    参考回答:

    AQS其实就是一个可以给我们实现锁的框架
    内部实现的关键是:先进先出的队列、state状态
    定义了内部类ConditionObject
    拥有两种线程模式独占模式和共享模式。
    在LOCK包中的相关锁(常用的有ReentrantLock、 ReadWriteLock)都是基于AQS来构建,一般我们叫AQS为同步器。

    ● 请简述一下线程池的运行流程,使用参数以及方法策略等

    考察点:线程池

    参考回答:

    线程池主要就是指定线程池核心线程数大小,最大线程数,存储的队列,拒绝策略,空闲线程存活时长。当需要任务大于核心线程数时候,就开始把任务往存储任务的队列里,当存储队列满了的话,就开始增加线程池创建的线程数量,如果当线程数量也达到了最大,就开始执行拒绝策略,比如说记录日志,直接丢弃,或者丢弃最老的任务。

    ● 线程,进程,然后线程创建有很大开销,怎么优化?

    考察点:多线程

    参考回答:

    可以使用线程池。

    ● 请介绍一下什么是生产者消费者模式?

    考察点:线程

    参考回答:

    生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。

    优点:支持并发、解耦。

    ● 请简述一下实现多线程同步的方法?

    考察点:多线程

    参考回答:

    可以使用synchronized、lock、volatile和ThreadLocal来实现同步。

    ● 如何在线程安全的情况下实现一个计数器?

    考察点:多线程

    参考回答:

    可以使用加锁,比如synchronized或者lock。也可以使用Concurrent包下的原子类。

    ● 多线程中的i++线程安全吗?请简述一下原因?

    考察点:多线程

    参考回答:

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

  • 相关阅读:
    重复打印文件首行n次
    考PMP证书总结
    @click @dblclick @keyup 等事件不起作用
    element-ui table 表格内出现一根横线???
    windows 鼠标拖动应用顶部移动 , 向下还原 , 自动最大化的设置
    【.NET】使用 XmlDocument 查找带命名空间的节点
    【WinForms】DataGridView自动调整列宽度以及最后一列宽度填充
    时间返回格式统一处理的几种办法
    hexo使用
    hexo搭建个人博客网站
  • 原文地址:https://www.cnblogs.com/sbclmy/p/10834880.html
Copyright © 2020-2023  润新知