渣渣小本求职复习之路每天一博客系列——Java基础(8)

  前情回顾:在上一章,我们回顾了IO的继承架构和几个常用的类,林信良老师在这几章传达的意思愈发强烈,那就是“学习这些标准API,要想不沦为死记硬背,应该先掌握API在设计时的封装、继承、多态结构。更进一步地,还可以从API中学习到良好设计的观念,有了这样的好习惯,以后对新的API或链接库就能更快掌握如何使用甚至改进。”

    我深以为然。

  刚才看球了,恒大夺冠,亚洲之巅!

——————————————————————————闲聊结束——————————————————————————

  第十一章:线程

  在这之前谈及的都是单线程程序,也就是启动的程序从main()程序进入点到结束只有一个流程。可是,我们常常会需要程序中可以有多个流程,也就是平常所说的多线程(Multi-thread)程序。

  第一节:Thread类与Runnable接口

  创建多线程的方法有两种,一种是实现Runnable接口,实现run()方法;另一种方法是继承Thread类,重写run()方法。其实,我们看看API说明文档或者源代码,就知道其实Thread类本身也实现了Runnable接口。

  所以,我们可以说:在java中,任何线程可执行的流程都要定义在Runnable的run()方法里。那既然是这样,我们是实现Runnable在run()中定义额外流程好,还是继承Thread在run()中定义额外流程好呢?

  实现接口的好处就是比较留有余地,还可以继承其他的类。如果继承了Thread,那么该类就是一种Thread,通常是为了直接调用Thread类中定义的一些方法,才会选择这样的实现方式。  

  第二节:线程的生命周期

  我们从最简单的开始,Daemon线程。

  主线程会从main()方法开始执行,直到main()方法结束后才会关闭JVM。如果主线程中启动了额外的线程,一般来说默认等待被启动的所有线程都执行完run()方法才关闭JVM。但是,如果一个Thread被设置为Daemon(守护)线程,在所有的非Daemon线程都结束时,JVM自动就会关闭。

  从main()方法开始的主线程就是一个非Daemon线程,所以说,要想把一个线程设置为Daemon线程或把一个Daemon线程设置为非Daemon线程,我们可以使用setDaemon()方法。那么,让我们来想一想,这个setDaemon()方法是在哪里定义的呢?

  根据源代码,我们发现setDaemon是定义在Thread类里的。那么,我的问题又来了。是否实现Runnable接口的类(非继承Thread类的子类)就不能设置为Daemon线程了呢?这个问题,小伙伴们可以想一想哦。

  值得注意的是,默认所有从Daemon线程产生的线程也都是Daemon线程,因为基本上由一个守护(服务)线程衍生出来的线程,也应该是为此而生的。

  然后,我们再来看看Thread基本状态图。在调用了start()方法后,有三个基本状态:可执行(Runnable)、被阻断(Blocked)、执行中(Running),状态间的转移如下图:

  看到这个图,我们实例化Thread并执行Start()之后,线程就会进入Runnable状态,这时候线程还没有真正开始执行run()方法,必须要等到排班器(Scheduler)调度到CPU执行,线程才会执行run()方法,进入Running状态。现在提一下其实我们的多线程并不是并行的,而是并发,看起来像是同时执行,但事实上在同一个时刻,一个CPU还是只能执行一个线程,只不过CPU会不断切换线程,且切换动作很快,所以看起来像是同时执行。

  线程其实是有权限权的,我们可以使用Thread的setPriority()方法设定优先权,可设定值为1(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY),默认是5(Thread.NORM_PRIORITY),超出1到10外的设定值会抛出异常(IllegalArgumentExeption)。数字越大优先权就越高,排班器就越优先为其调度。

  有几种状况会让线程进入到阻塞(Blocked)状态,例如调用Thread.sleep()方法,就会让线程阻塞(其他的,还有进入synchronized钱竞争对象锁定的阻断、调用wait()的阻断等);等待输入/输出完成也会进入Blocked。使用多线程,就是当某线程进入Blocked时,让另一线程调度CPU执行(进如Running状态),避免CPU空闲时间过多,是改进效能的常用方式之一。

  线程因输入/输出进入Blocked状态,在完成输入/输出后,会回到Runnable状态,等待Schedule调度执行(Running状态)。一个进入Blocked状态,若此时有其他线程调用了该线程的interrupt()方法,就会抛出InterruptedException异常对象,这是让线程“醒过来”的方式。

  有一种策略叫做安插线程。如果A线程正在运行,流程中允许B线程加入,等到B线程执行完毕后再继续A线程的流程,我们可以使用join()方法。这就好比,你在吃饭,突然有一个紧急电话打进来等电话打完了之后,你才再继续吃饭。看一段代码:

 1 package cc.openhome;
 2 
 3 public class JoinDemo {
 4     public static void main(String[] args) {
 5         System.out.println("Main thread 开始...");
 6         Thread threadB = new Thread() {
 7             @Override
 8             public void run() { 
 9                 try { 
10                     System.out.println("Thread B 开始..."); 
11                     for(int i = 0; i < 5; i++) { 
12                         Thread.sleep(1000); 
13                         System.out.println("Thread B 执行..."); 
14                     }
15                     System.out.println("Thread B 将结束..."); 
16                 } 
17                 catch(InterruptedException e) { 
18                     e.printStackTrace(); 
19                 } 
20             } 
21         };
22         
23         threadB.start();
24 
25         try {
26             // Thread B 加入 Main thread 流程
27             threadB.join();
28         } 
29         catch(InterruptedException e) { 
30             e.printStackTrace(); 
31         } 
32         System.out.println("Main thread 將结束...");
33     }    
34 }

  那么,我们要如何停止线程呢?线程完成run()方法后,就会进入Dead,进入Dead(或已经调用过start()方法)的线程不可以再次调用start()方法,否则会抛出IllegalThreadStateException。

  Thread类上定义有stop()方法,不过被标示Deprecated。被标示为Deprecated的API,表示过去确实定义过,后来因为会引发某些问题,为了确保向前兼容性,这些API没有直接剔除,但不建议新撰写的程序再使用它。其实,最好的方法就是,让线程跑完应有的流程,进入死亡状态。

  第三节:关于ThreadGroup

  在多线程编程中,有时候会有很多线程需要管理,这时候,让我们隆重介绍ThreadGroup线程群组。

  每个线程产生时,都会被归入某个线程群组。如果没有指定,则会被归入到产生该子线程的线程群组中取。值得注意的是,线程一旦被归入到某个群组,就无法更换群组。

  我们可以用java.lang.ThreadGroup类来管理群组中的线程。可以使用以下方式产生群组,并在产生线程时指定所属群组:

1 ThreadGroup t1=new ThreadGroup("group1");
2 ThreadGroup t2=new ThreadGroup("group2");
3 //或者是下面那样
4 Thread thread1=new Thread(threadGroup1,"group1's member");
5 Thread thread2=new Thread(threadGroup2,"group2's member");

  ThreadGroup的某些方法,可以对群组中所有线程产生作用。例如,interrupt()方法可以中断群组中所有的线程,setMaxPriority()方法可以设定群组中所有线程最大优先权(本来就拥有更高优先权的线程不受影响)。更多的方法,大家可以参考API说明文档,我更推荐在看完文档之后再去瞅瞅源代码!

  

  今天的线程就回顾到这里,明天的博客继续关注线程。

———————————————————————第二十二天————————————————————

  Linux很有意思。

1.之前虽然有碰过一两次,不过基本都没怎么操作。

2.这两天因为毕设的缘故,接触得比较多,嘻嘻,突然发现各种便利,各种高效,各种高大上吖!!!

3.考虑近期就进行Linux的学习哦。大家有什么好资料介绍一下么?实体书、电子书、博客什么的都可以哟。

posted @ 2013-11-09 23:22  LevenYes  阅读(331)  评论(0编辑  收藏  举报