线程(二)

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

考察点: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
{
  
     public static void main(String[] args)
     {
         CallableThreadTest ctt =  new CallableThreadTest();
         FutureTask 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++,执行期中任何一步都是有可能被其他线程抢占的。

转载于:https://www.cnblogs.com/sbclmy/p/10834880.html

你可能感兴趣的:(java)