简单了解Java编程中线程的创建与守护线程

线程的两种创建方式及优劣比较
1、通过实现Runnable接口线程创建
(1).定义一个类实现Runnable接口,重写接口中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。
(2).创建Runnable接口实现类的对象。
(3).创建一个Thread类的对象,需要封装前面Runnable接口实现类的对象。(接口可以实现多继承)
(4).调用Thread对象的start()方法,启动线程
示例代码:

package demo.thread; 

public class TreadDemo1 implements Runnable {
  private int countDown = 10;
  @Override
  // 在run方法中定义任务
  public void run() {
    while (countDown-- > 0) {
      System.out.println("#" + Thread.currentThread().getName() + "("
          + countDown + ")");
    }
  } 

  public static void main(String[] args) {
    // Runnable中run方法是一个空方法,并不会产生任何线程行为,必须显式地将一个任务附着到线程上
    TreadDemo1 tt=new TreadDemo1();
    new Thread(tt).start();
    new Thread(tt).start();
    System.out.println("火箭发射前倒计时:");
  }
}

运行结果:
火箭发射前倒计时:

#Thread-1(8)
#Thread-1(7)
#Thread-1(6)
#Thread-1(5)
#Thread-1(4)
#Thread-1(3)
#Thread-1(2)
#Thread-1(1)
#Thread-1(0)
#Thread-0(9)

2、通过继承Thread类创建线程
(1).首先定义一个类去继承Thread父类,重写父类中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。
(2).直接创建一个ThreadDemo2类的对象,也可以利用多态性,变量声明为父类的类型。
(3).调用start方法,线程t启动,隐含的调用run()方法。
示例代码:

package demo.thread; 

public class ThreadDemo2 extends Thread {
  private int countDown = 10; 

  @Override
  // 在run方法中定义任务
  public void run() {
    while (countDown-- > 0) {
      System.out.println("#" + this.getName() + "(" + countDown + ")");
    }
  } 

  public static void main(String[] args) {
    new ThreadDemo2().start();
    new ThreadDemo2().start();
    // 由于start方法迅速返回,所以main线程可以执行其他的操作,此时有两个独立的线程在并发运行
    System.out.println("火箭发射前倒计时:");
  }
}

运行结果:

#Thread-0(9)
#Thread-0(8)
#Thread-0(7)
#Thread-0(6)
#Thread-0(5)
#Thread-0(4)
#Thread-0(3)
#Thread-0(2)
#Thread-0(1)
#Thread-0(0)

火箭发射前倒计时:

#Thread-1(9)
#Thread-1(8)
#Thread-1(7)
#Thread-1(6)
#Thread-1(5)
#Thread-1(4)
#Thread-1(3)
#Thread-1(2)
#Thread-1(1)
#Thread-1(0)

3、两种方式的比较
首先分析两种方式的输出结果,同样是创建了两个线程,为什么结果不一样呢?
使用实现Runnable接口方式创建线程可以共享同一个目标对象 (TreadDemo1 tt=new TreadDemo1();),实现了多个相同线程处理同一份资源。
然后再看一段来自JDK的解释:
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run 的无参数方法。
设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread 类实现了Runnable 。激活的意思是说某个线程已启动并且尚未停止。
此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下,如果只想重写run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。
 
采用继承Thread类方式:
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

后台线程(守护线程)
所谓的后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此当所有的非后台线程结束时,程序也就终止了,同时会杀死所有后台线程。反过来说,只要有任何非后台线程(用户线程)还在运行,程序就不会终止。后台线程在不执行finally子句的情况下就会终止其run方法。后台线程创建的子线程也是后台线程。 下面是一个后台线程的示例:

package demo.thread; 

import java.util.concurrent.TimeUnit; 

public class DaemonDemo implements Runnable {
  @Override
  public void run() {
    try {
      while (true) {
        Thread.sleep(1000);
        System.out.println("#" + Thread.currentThread().getName());
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {// 后台线程不执行finally子句
      System.out.println("finally ");
    }
  } 

  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      Thread daemon = new Thread(new DaemonDemo());
      // 必须在start之前设置为后台线程
      daemon.setDaemon(true);
      daemon.start();
    }
    System.out.println("All daemons started");
    try {
      TimeUnit.MILLISECONDS.sleep(1000);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

运行结果:

All daemons started
#Thread-2
#Thread-3
#Thread-1
#Thread-0
#Thread-9
#Thread-6
#Thread-8
#Thread-5
#Thread-7
#Thread-4

分析:从结果可以看出,十个子线程并没有无线循环的打印,而是在主线程(main())退出后,JVM强制关闭所有后台线程。而不会有任何希望出现的确认形式,如finally子句不执行。

(0)

相关推荐

  • Java线程之守护线程(Daemon)用法实例

    本文实例讲述了Java线程之守护线程(Daemon)用法.分享给大家供大家参考.具体如下: 守护线程(Daemon) Java有两种Thread:"守护线程Daemon"与"用户线程User". 我们之前看到的例子都是用户,守护线程是一种"在后台提供通用性支持"的线程,它并不属于程序本体. 从字面上我们很容易将守护线程理解成是由虚拟机(virtual machine)在内部创建的,而用户线程则是自己所创建的.事实并不是这样,任何线程都可以是&qu

  • Java 守护线程_动力节点Java学院整理

    估计学过Unix开发但是没有细致学习Java的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说平台的设计者多多少少是收到Unix思想的影响,而守护线程机制又是对JVM这样的平台凑合,于是守护线程应运而生. Daemon的作用是为其他线程的运行提供服务,比如说GC线程.其实User Thread线程和Daemo

  • 浅谈java中守护线程与用户线程

    Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程是一个用户线程,这个是我们可以看到的线程,其实JVM内部同时还启动了好多守护线程,比如垃圾回收线程.那么守护线程和用户线程有什么区别那?区别之一是当最后一个非守护线程结束时候,JVM会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出.言外之意是只要有一个用户线程还没结束正常情况下JVM就不会退出. 那么Java中如何创建一个守

  • Java并发编程示例(七):守护线程的创建和运行

    Java有一种特殊线程,守护线程,这种线程优先级特别低,只有在同一程序中的其他线程不执行时才会执行. 由于守护线程拥有这些特性,所以,一般用为为程序中的普通线程(也称为用户线程)提供服务.它们一般会有一个无限循环,或用于等待请求服务,或用于执行任务等.它们不可以做任何重要的工作,因为我们不确定他们什么时才能分配到CPU运行时间,而且当没有其他线程执行时,它们就会自动终止.这类线程的一个典型应用就是Java的垃圾回收. 在本节示例中,我们将创建两个线程,一个是普通线程,向队列中写入事件:另外一个是

  • Java守护线程实例详解_动力节点Java学院整理

    在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆: 只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作:只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作. Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者. User和Daemon两者几乎没有区别,唯一的不同之处就在

  • 简单了解Java编程中线程的创建与守护线程

    线程的两种创建方式及优劣比较 1.通过实现Runnable接口线程创建 (1).定义一个类实现Runnable接口,重写接口中的run()方法.在run()方法中加入具体的任务代码或处理逻辑. (2).创建Runnable接口实现类的对象. (3).创建一个Thread类的对象,需要封装前面Runnable接口实现类的对象.(接口可以实现多继承) (4).调用Thread对象的start()方法,启动线程 示例代码: package demo.thread; public class Tread

  • 简单介绍Java编程中的线程池

    从 Java 5 开始,Java 提供了自己的线程池.线程池就是一个线程的容器,每次只执行额定数量的线程. java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.它很灵活,但使用起来也比较复杂,本文就对其做一个介绍. 首先是构造函数.以最简单的构造函数为例: public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit uni

  • 简单了解Java编程中抛出异常的方法

    任何Java代码都可以抛出异常,如:自己编写的代码.来自Java开发环境包中代码,或者Java运行时系统.无论是谁,都可以通过Java的throw语句抛出异常.从方法中抛出的任何异常都必须使用throws子句. 1. throws抛出异常 如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常.例如汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理. throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Ex

  • 简单了解Java编程中对异常处理的运用

    Java异常是一个描述在代码段中发生的异常(也就是出错)情况的对象.当异常情况发生,一个代表该异常的对象被创建并且在导致该错误的方法中被抛出(throw).该方法可以选择自己处理异常或传递该异常.两种情况下,该异常被捕获(caught)并处理.异常可能是由Java运行时系统产生,或者是由你的手工代码产生.被Java抛出的异常与违反语言规范或超出Java执行环境限制的基本错误有关.手工编码产生的异常基本上用于报告方法调用程序的出错状况. Java异常处理通过5个关键字控制:try.catch.th

  • Java编程中实现Condition控制线程通信

    java中控制线程通信的方法 1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信.不灵活. 2.利用Condition控制线程通信,灵活. 3.利用管道pipe进行线程通信,不推荐 4.利用BlockingQueue控制线程通信 本文就讲解利用Condition控制线程通信,非常灵活的方式. Condition类是用来保持Lock对象的协调调用. 对Lock不了解的可以参考:Java线程同步Lock同步锁代码示例

  • Java编程中避免equals方法的隐藏陷阱介绍

    摘要 本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性. 在<Effective Java>的第8项中,Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题,要保证派生类的equal正确性语义所会面对的困难.Bloch这样写到: 除非你忘记了面向对象抽象的好处,否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确 在<Programming in Scala>中的第28章演示

  • Java编程中10个最佳的异常处理技巧

    在实践中,异常处理不单单是知道语法这么简单.编写健壮的代码是更像是一门艺术,在本文中,将讨论Java异常处理最佳实践.这些Java最佳实践遵循标准的JDK库,和几个处理错误和异常的开源代码.这还是一个提供给java程序员编写健壮代码的便利手册.Java 编程中异常处理的最佳实践 这里是我收集的10个Java编程中进行异常处理的10最佳实践.在Java编程中对于检查异常有褒有贬,强制处理异常是一门语言的功能.在本文中,我们将尽量减少使用检查型异常,同时学会在Java编程中使用检查型VS非检查型异常

  • java编程中自动拆箱与自动装箱详解

    什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象实例的时候,我们会这样: Class a = new Class(parameter); 当我们创建一个Integer对象时,却可以这样: Integer i = 100; (注意:不是 int i = 100; ) 实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = Integer.valueOf(100); (感谢@

  • Java编程中的构造函数详细介绍

    本文主要是为新手.对java语言感兴趣的人和那些没有系统学习过java基础知识的人进行一个总结,在文章中对构造函数进行了较为详细的说明和讨论,也包含了我个人对于java面向对象中构造函数的一些看法.希望走在java学习道路上的同行者可以有一个较为清晰的认知和理解.当然仅为个人观点,水平有限,不足之处,还请大家多多指出,互相交流学习. 1.构造函数的概念 很多java新手谈到构造函数就会犯晕,我们先来看看什么是构造函数. 首先,构造函数是函数的一种特殊形式,特殊在哪里?构造函数中不需要定义返回类型

  • java编程中实现调用js方法分析

    本文实例讲述了java编程中实现调用js方法.分享给大家供大家参考,具体如下: /* * 加载脚本引擎,并在java中调用js方法 */ public void test2() { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); try { String str="2&1"

随机推荐