Java多线程之线程的启动
Java多线程之线程的启动
一、前言
启动线程的方法有如下两种。
- 利用Thread 类的子类的实例启动线程
- 利用Runnable 接口的实现类的实例启动线程
最后再介绍下java.util.concurrent.ThreadFactory中的线程创建
下面分别做以介绍
二、利用Thread 类的子类启动线程
这里来学习一下利用Thread 类的子类的实例来启动线程的方法,即上一篇博文中使用的方法。我们构造一个PrintThread 类表示输出1000次指定字符串的线程。输出的字符串通过构造函数的参数传入,并赋给message 字段。PrintThread 类被声明为Thread 的子类。
如下表示输出10 000 次指定字符串的线程的类PrintThread(PrintThread.java)
public class PrintThread extends Thread {
private String message;
public PrintThread(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(message);
}
}
}
Main 类是用于创建上面声明的PrintThread 类的两个实例并利用它们来启动两个线程的程序。
利用PrintThread 类启动2 个线程(Main.java)
public class Main {
public static void main(String[] args) {
new PrintThread("Good!").start();
new PrintThread("Nice!").start();
}
}
main 方法创建了PrintThread 类的实例,并直接(不赋给某个变量)调用了该实例的start 方法。
start 方法会启动新的线程,然后由启动的新线程调用PrintThread 类的实例的run 方法。
最终结果就是由新启动的线程执行1000次Good! 字符串输出。
为了程序简洁,上面的程序只用一条语句启动了线程。但实际上,“创建PrintThread 的实例”和“启动该实例对应的线程”是两个完全不同的处理。也就是说,即便已经创建了实例,但是如果不调用start 方法,线程也不会被启动。上面这条语句也可以像下面这样写成两句。
另外,这里再提醒大家注意,“PrintThread 的实例”和“线程本身”不是同一个东西。即便创建了PrintThread 的实例,线程也并没有启动,而且就算线程终止了,PrintThread 的实例也不会消失。
主线程在Main 类的main 方法中启动了两个线程。随后main 方法便会终止,主线程也会跟着终止。但整个程序并不会随之终止,因为启动的两个线程在字符串输出之前是不会终止的。直到所有的线程都终止后,程序才会终止。也就是说,当这两个线程都终止后,程序才会终止。
关于程序的终止:
Java 程序的终止是指除守护线程(Daemon Thread)以外的线程全部终止。守护线程是执行后台作业的线程。我们可以通过setDaemon 方法把线程设置为守护线程。
创建Thread 类的子类、创建子类的实例、调用start 方法——这就是利用Thread 类的子类启动线程的方法。
三、利用Runnable 接口启动线程
这里来学习一下利用Runnable 接口的实现类的实例来启动线程的方法。Runnable 接口包含在java.lang 包中,声明如下。
public interface Runnable {
public abstract void run();
}
Runnable 接口的实现类必须要实现run 方法,否则要声明为抽象方法。
现在我们来构造Printer类表示一个输出10 000 次指定字符串的线程。输出的字符串通过构造函数的参数传入,并赋给message 字段。由于Printer 类实现(implements)了Runnable 接口,所以此时也就无需再将Printer 类声明为Thread 类的子类。
输出指定字符串的Printer 类(Printer.java)
public class Printer implements Runnable {
private String message;
public Printer(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(message);
}
}
}
Main 类是用于创建两个Printer 类的实例,并利用它们来启动两个线程的程序。
利用Runnable 接口启动两个线程(Main.java)
public class Main {
public static void main(String[] args) {
new Thread(new Printer("Good")).start();
new Thread(new Printer("Nice")).start();
}
}
创建Thread 的实例时,构造函数的参数中会传入Printer 类的实例,然后会调用start 方法,启动线程。
start 方法会启动新的线程,然后由启动的新线程调用Printer 类的实例的run 方法。最终结果就是由新启动的线程执行1000次Good! 字符串输出。上面这条语句也可以像下面这样写成三句。
创建Runnable 接口的实现类,将实现类的实例作为参数传给Thread 的构造函数,调用start 方法——这就是利用Runnable 接口启动线程的方法。
不管是利用Thread 类的子类的方法(1),还是利用Runnable 接口的实现类的方法(2),启动新线程的方法最终都是Thread 类的start 方法。
关于Thread 类和Runnable 方法
Thread 类本身还实现了Runnable 接口,并且持有run 方法,但Thread 类的run 方法主体是空的,不执行任何操作。Thread 类的run 方法通常都由子类的run 方法重写(override)。
四、java.util.concurrent.ThreadFactory 中的线程创建
java.util.concurrent 包中包含一个将线程创建抽象化的ThreadFactory 接口。利用该接口,我们可以将以Runnable 作为传入参数并通过new 创建Thread 实例的处理隐藏在ThreadFactory 内部。典型用法如下所示。默认的ThreadFactory 对象是通过Executors.defaultThreadFactory 方法获取的。
此处运行的Printer类与上面的Printer 类相同
利用ThreadFactory 新启动线程(Main.java)
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Main2 {
public static void main(String[] args) {
ThreadFactory factory = Executors.defaultThreadFactory();
factory.newThread(new Printer("Good!")).start();
for (int i = 0; i < 1000; i++) {
System.out.println("Nice!");
}
}
}
参考:图解Java多线程设计模式
Java多线程之线程的启动的更多相关文章
- Java多线程之线程其他类
Java多线程之线程其他类 实际编码中除了前面讲到的常用的类之外,还有几个其他类也有可能用得到,这里来统一整理一下: 1,Callable接口和Future接口 JDK1.5以后提供了上面这2个接口, ...
- Java多线程之线程的同步
Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同 ...
- Java多线程之线程的控制
Java多线程之线程的控制 线程中的7 种非常重要的状态: 初始New.可运行Runnable.运行Running.阻塞Blocked.锁池lock_pool.等待队列wait_pool.结束Dea ...
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
- java多线程与线程间通信
转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...
- Java多线程之线程的生命周期
Java多线程之线程的生命周期 一.前言 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(R ...
- Java多线程| 01 | 线程概述
Java多线程| 01 | 线程概述 线程相关概念 进程与线程 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...
- Java多线程之线程的通信
Java多线程之线程的通信 在总结多线程通信前先介绍一个概念:锁池.线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池.每个对象都有自己的锁池的空间,用于放置等待运行的线程.这些 ...
- Java多线程父子线程关系 多线程中篇(六)
有的时候对于Java多线程,我们会听到“父线程.子线程”的概念. 严格的说,Java中不存在实质上的父子关系 没有方法可以获取一个线程的父线程,也没有方法可以获取一个线程所有的子线程 子线程的消亡与父 ...
随机推荐
- linux初学者-CIFS网络文件系统篇
linux初学者-CIFS网络文件系统篇 CIFS是一种通用网络文件系统,主要用于网络设备之间的文件共享.CIFS可以在linux系统和windows系统之间共享文件,因此这种文件系统主要用于客户端是 ...
- 《SQL错题》
(选择一项) A: B: C: D: 正确答案是 D (选择一项) A: B: C: D: 正确答案是 A (选择一项) 8 A: B: C: ...
- 使用ASM实现动态代理
如果对我这段代码感兴趣,直接拷贝测试debug,要不然你不知道我写的是什么鬼,如果有什么问题,可以告诉我. 一.实现动态代理,首先得考虑有应该定义哪些类,根据JDK的动态代理思想,那么它就应该有一个生 ...
- 阅读nopcommerce startup源码
创建一个asp.net core项目,可以到到startup类有两个方法 // This method gets called by the runtime. Use this method to a ...
- python传递参数
1.脚本 # -*- coding: utf-8 -*- from sys import argvscript, first,second = argv #将命令中输入的参数解包后传递给左边 age ...
- TCP加速机制是如何加速的?
一.什么是TCP加速? TCP加速就是在高时延链路提高吞吐量的一系列解决方案. 二.为什么需要对TCP进行加速? 1.传统的TCP拥塞控制算法并不适用于高时延.高误码的链路. 2.随着we ...
- Linux下安装配置Jmeter5.1,并执行jmx文件
Windows下的jmeter是GUI模式,可查看操作,但是GUI对性能的干扰比较大,所有一般压测会在Linux上运行. 下面是Linux下安装配置Jmeter5.1,并执行jmx文件的步骤, 一.安 ...
- Hadoop MapReduce的Shuffle过程
一.概述 理解Hadoop的Shuffle过程是一个大数据工程师必须的,笔者自己将学习笔记记录下来,以便以后方便复习查看. 二. MapReduce确保每个reducer的输入都是按键排序的.系统执行 ...
- codeforces 327 B. Hungry Sequence
题目链接 题目就是让你输出n个数的序列,要保证该序列是递增的,并且第i个数的前面不能保护它的约数,我直接先对前100000的素数打表,然后输出前n个,so easy. //cf 191 B #incl ...
- 动态开内存(malloc与calloc)
malloc与calloc 1.函数原型 #include<stdlib.h> void *malloc(unsigned int size); //申请size字节的内存 voi ...