Java面试09|多线程

15个顶级Java多线程面试题及回答 http://ifeve.com/15-java-faq/

1、关于线程的状态及相互转换

(1)join()方法:能够使线程顺序执行。可以将一个任务分为多个子任务,调用join()方法等待这些子任务完成运算,然后汇总结果。需要注意这并不能保证各个子任务线程的结束顺序。

(2)线程只能从就绪状态(runnable)这一条唯一路径到运行状态(running)

(3)Thread类,定义了一些列的线程操作函数。例如,sleep()休眠, interrupt()中断, getName()获取线程名称等

(4)在启动线程时调用start()方法,重复调用会抛出线程状态异常。调用run()就相当于调用了普通方法

(5)在线程调用wait()方法进入阻塞状态,再次被唤醒时继续执行wait()方法后面的代码

(6)wait()会释放锁,而yield()和sleep()不会释放锁

(7)注意interrupt()方法被调用后状态由等待blocked进入锁定blocked,这在AQS中的公平锁中需要处理这种情况。

 

 

2、线程中断的几个方法

(1)public void interrupt() 中断目标线程

(2)public boolean isInterrupted() 返回中断状态

(3)public static boolean interrupted() 静态方法,清除中断状态并返回之前的值

阻塞库方法,Thread.sleep()和Object.wait()等,都会检查线程何时中断,并且发现中断时提前返回,它们在响应中断时执行的操作包括:清除中断状态、抛出InterruptedException,表示阻塞操作由于中断而提前结束。

可以总结下:

(1)处于blocked状态的线程:清除中断状态、抛出InterruptedException,表示阻塞操作由于中断而提前结束

(2)处于runnable或者running状态时,线程的中断标记会被设置为true

(3)处于dead状态时不会产生任何操作

 

 

上面的题目一定要看,我在这里筛选及补充了几个题目:

3、假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?

把相互独立的计算任务包含在一个工作单元内,无需为每个单元启动新的线程。这样处理多线程代码通常效率更高。因为不用去为每个计算单元单独启动Thread线程。执行代码的线程是重用的。

(1)任务

Callable代表了一段可以调用并返回结果的代码

Future接口用来表示异步任务,是还没有完成的任务给出的未来结果。主要方法有get()、cancel()和isDone()

FutureTask是Future接口的常用实现类,它也实现了Runnable接口,所以和Runnable和Callable一样,可以由执行者高度。

(2)执行者 通过Executors类的工厂方法获取众多执行者之一

有个关于FutureTask的好例子,如下:

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
public  class  FutureTaskExample { 
      public  static  void  main(String[] args) { 
         MyCallable callable1 =  new  MyCallable( 1000 );                        // 要执行的任务 
         MyCallable callable2 =  new  MyCallable( 2000 ); 
   
         FutureTask<String> futureTask1 =  new  FutureTask<String>(callable1); // 将Callable写的任务封装到一个由执行者调度的FutureTask对象 
         FutureTask<String> futureTask2 =  new  FutureTask<String>(callable2); 
    
         ExecutorService executor = Executors.newFixedThreadPool( 2 );         // 创建线程池并返回ExecutorService实例 
         executor.execute(futureTask1);   // 执行任务 
         executor.execute(futureTask2);   
            
         while  ( true ) { 
             try 
                 if (futureTask1.isDone() && futureTask2.isDone()){ //  两个任务都完成 
                     System.out.println( "Done" ); 
                     executor.shutdown();                           // 关闭线程池和服务  
                     return
                
                    
                 if (!futureTask1.isDone()){  // 任务1没有完成,会等待,直到任务完成 
                     System.out.println( "FutureTask1 output=" +futureTask1.get()); 
                
                    
                 System.out.println( "Waiting for FutureTask2 to complete" ); 
                 String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); 
                 if (s != null ){ 
                     System.out.println( "FutureTask2 output=" +s); 
                
             catch  (InterruptedException | ExecutionException e) { 
                 e.printStackTrace(); 
             } catch (TimeoutException e){ 
                 //do nothing 
            
        
    

  

示例如下: 

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
public  class  Sums {
 
     // 使用Callable比Runnable更优势的地方在于Callable可以有确切的返回值。
     static  class  Sum  implements  Callable<Long> {
         private  final  long  from;
         private  final  long  to;
 
         Sum( long  from,  long  to) {
             this .from = from;
             this .to = to;
         }
 
         @Override
         public  Long call() {
             long  acc =  0 ;
             for  ( long  i = from; i <= to; i++) {
                 acc = acc + i;
             }
             return  acc;
         }
     }
 
     public  static  void  main(String[] args)  throws  Exception {
         ExecutorService executor = Executors.newFixedThreadPool( 2 );
         
         // Executes the given tasks, returning a list of Futures holding their status and results when all complete
         List<Future<Long>> results = executor.invokeAll(
                                                            asList(
                                                                 new  Sum( 0 10 ),
                                                                 new  Sum( 100 , 1_000),
                                                                 new  Sum(10_000, 1_000_000)
                                                             ));
         // 另外要注意executor服务必须被关闭。如果它没有被关闭,主方法执行完后JVM就不会退出,因为仍然有激活线程存在
         executor.shutdown();
 
         for  (Future<Long> result : results) {
             // Waits if necessary for the computation to complete, and then retrieves its result.
             System.out.println(result.get());
         }
     }
}

 书写如上程序时需要注意三点:

(1)Callable是接口,我们需要重写的方法为call()

(2)线程池用完一定要shutdown()

 

4、分析线程池的实现原理和任务的调度过程

关于线程池的实现原理可以参考如下:

(1)一定要看 http://www.jianshu.com/p/87bff5cc8d8c

(1)http://blog.csdn.net/mazhimazh/article/details/19243889

(2)http://blog.csdn.net/mazhimazh/article/details/19283171

(3)参考《Java特种兵》295页内容

线程池实现原理就是线程池与工作队列的组合,在Executor任务执行框架中就体现了这种模式。

一个简单的小例子。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package  ThreadPool;
 
import  java.util.LinkedList;
import  java.util.List;
 
/**
  * 线程池类,线程管理器:创建线程,执行任务,销毁线程,获取线程基本信息
  */
public  final  class  ThreadPool {
     // 线程池中默认线程的个数为5
     private  static  int  worker_num =  5 ;
     // 工作线程
     private  WorkThread[] workThrads;
     // 未处理的任务
     private  static  volatile  int  finished_task =  0 ;
     // 任务队列,作为一个缓冲,List线程不安全
     private  List<Runnable> taskQueue =  new  LinkedList<Runnable>();
     private  static  ThreadPool threadPool;
 
     // 创建具有默认线程个数的线程池
     private  ThreadPool() {
         this ( 5 );
     }
 
     // 创建线程池,worker_num为线程池中工作线程的个数
     private  ThreadPool( int  worker_num) {
         ThreadPool.worker_num = worker_num;
         workThrads =  new  WorkThread[worker_num];
         for  ( int  i =  0 ; i < worker_num; i++) {
             workThrads[i] =  new  WorkThread();
             workThrads[i].start(); // 开启线程池中的线程
         }
     }
 
     // 单态模式,获得一个默认线程个数的线程池
     public  static  ThreadPool getThreadPool() {
         return  getThreadPool(ThreadPool.worker_num);
     }
 
     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
     // worker_num<=0创建默认的工作线程个数
     public  static  ThreadPool getThreadPool( int  worker_num1) {
         if  (worker_num1 <=  0 )
             worker_num1 = ThreadPool.worker_num;
         if  (threadPool ==  null )
             threadPool =  new  ThreadPool(worker_num1);
         return  threadPool;
     }
 
     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public  void  execute(Runnable task) {
         synchronized  (taskQueue) {
             taskQueue.add(task);
             taskQueue.notify();
         }
     }
 
     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public  void  execute(Runnable[] task) {
         synchronized  (taskQueue) {
             for  (Runnable t : task)
                 taskQueue.add(t);
             taskQueue.notify();
         }
     }
 
     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public  void  execute(List<Runnable> task) {
         synchronized  (taskQueue) {
             for  (Runnable t : task)
                 taskQueue.add(t);
             taskQueue.notify();
         }
     }
 
     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
     public  void  destroy() {
         while  (!taskQueue.isEmpty()) { // 如果还有任务没执行完成,就先睡会吧
             try  {
                 Thread.sleep( 10 );
             catch  (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         // 工作线程停止工作,且置为null
         for  ( int  i =  0 ; i < worker_num; i++) {
             workThrads[i].stopWorker();
             workThrads[i] =  null ;
         }
         threadPool= null ;
         taskQueue.clear(); // 清空任务队列
     }
 
     // 返回工作线程的个数
     public  int  getWorkThreadNumber() {
         return  worker_num;
     }
 
     // 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成
     public  int  getFinishedTasknumber() {
         return  finished_task;
     }
 
     // 返回任务队列的长度,即还没处理的任务个数
     public  int  getWaitTasknumber() {
         return  taskQueue.size();
     }
 
     // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数
     @Override
     public  String toString() {
         return  "WorkThread number:"  + worker_num +  "  finished task number:"
                 + finished_task +  "  wait task number:"  + getWaitTasknumber();
     }
 
     /**
      * 内部类,工作线程
      */
     private  class  WorkThread  extends  Thread {
         // 该工作线程是否有效,用于结束该工作线程
         private  boolean  isRunning =  true ;
 
         /*
          * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
          */
         @Override
         public  void  run() {
             Runnable r =  null ;
             while  (isRunning) { // 注意,若线程无效则自然结束run方法,该线程就没用了
                 synchronized  (taskQueue) {  // 在这里提供了同步
                     while  (isRunning && taskQueue.isEmpty()) { // 队列为空
                         try  {
                             taskQueue.wait( 20 );
                         catch  (InterruptedException e) {
                             e.printStackTrace();
                         }
                     }
                     if  (!taskQueue.isEmpty())
                         r = taskQueue.remove( 0 ); // 取出任务
                 }
                 if  (r !=  null ) {
                     r.run(); // 执行任务
                 }
                 finished_task++;
                 r =  null ;
             }
         } // end run
 
         // 停止工作,让该线程自然执行完run方法,自然结束
         public  void  stopWorker() {
             isRunning =  false ;
         }
     }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//测试线程池 
public  class  TestThreadPool {
     public  static  void  main(String[] args) {
         // 创建3个线程的线程池
         ThreadPool t = ThreadPool.getThreadPool( 3 );
         t.execute( new  Runnable[] {  new  Task(),  new  Task(),  new  Task() });
         t.execute( new  Runnable[] {  new  Task(),  new  Task(),  new  Task() });
         System.out.println(t);
         t.destroy(); // 所有线程都执行完成才destory
         System.out.println(t);
     }
 
     // 任务类
     static  class  Task  implements  Runnable {
         private  static  volatile  int  i =  1 ;
         @Override
         public  void  run() { // 执行任务
             System.out.println( "任务 "  + (i++) +  " 完成" );
         }
     }
}

 

Java线程的调度分为协同式线程调度和抢占式线程调度。

ScheduleThreadPoolExecutor是Java提供的多线程调度器,它可以接收任务,并把它们安排给线程池里的线程。可以参考如下内容:

《Java特种兵》 306页

利用了多线程加上任务资源共享的方式来实现服务器端大量任务的调度。

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值