zoukankan      html  css  js  c++  java
  • 渣渣小本求职复习之路每天一博客系列——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的学习哦。大家有什么好资料介绍一下么?实体书、电子书、博客什么的都可以哟。

  • 相关阅读:
    一个实现编译次数记录的jsfl
    特殊的RSS图标设置,您可以免费使用
    jquery实现的视差滚动教程(视差大背景效果)
    40个免费的wordpress主题推荐
    php+mysql方便的查询
    jQuery 简单实现select二级联动
    我对Oracle的刷未提交数据到文件的学习体会
    dbms_output.put_line的小例子
    isqlplus 的 define 与 pl/sql 的 &
    PLSQL 的 for循环的小例子
  • 原文地址:https://www.cnblogs.com/levenyes/p/3416233.html
Copyright © 2011-2022 走看看