Java多线程3:Thread中的实例方法
一、Thread类中的方法调用方式
学习Thread类中的方法是学习多线程的第一步。在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别:
1、this.XXX()
这种调用方式表示的线程是线程实例本身
2、Thread.currentThread.XXX()或Thread.XXX()
上面两种写法是一样的意思。这种调用方式表示的线程是正在执行Thread.currentThread.XXX()所在代码块的线程
当然,这么说,肯定有人不理解两者之间的差别。没有关系,之后会讲清楚,尤其是在讲Thread构造函数这块。讲解后,再回过头来看上面2点,会加深理解。
二、Thread类中的实例方法
从Thread类中的实例方法和类方法的角度讲解Thread中的方法,这种区分的角度也有助于理解多线程中的方法。实例方法,只和实例线程(也就是new出来的线程)本身挂钩,和当前运行的是哪个线程无关。看下Thread类中的实例方法:
1、start()方法
start()方法的作用讲得直白点就是通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象的run()方法,产生一个异步执行的效果。补充知识点:怎样理解阻塞非阻塞与同步异步的区别?
举例:
public class Thread01 implements Runnable{
@Override
public void run() {
for(int i = 0; i < 5; i++) {
try {
Thread.sleep((int) Math.random() * 5000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
Runnable runnable = new Thread01();
Thread thread = new Thread(runnable);
thread.start();
for(int i = 0; i < 5; i++) {
try {
Thread.sleep((int) Math.random() * 5000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
第一次运行结果:
Thread-0
main
Thread-0
main
Thread-0
main
Thread-0
main
Thread-0
main
第二次运行结果:
main
Thread-0
main
Thread-0
main
Thread-0
Thread-0
Thread-0
main
main
可以看到,CPU调用哪个线程具有不确定性。再举例说明start()方法的顺序是否就是线程启动的顺序?
public class Thread01 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
Runnable runnable = new Thread01();
//创建线程1
Thread thread1 = new Thread(runnable);
//创建线程2
Thread thread2 = new Thread(runnable);
//创建线程3
Thread thread3 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
}
}
第一次运行的结果:
Thread-1
Thread-0
Thread-2
第二次运行的结果:
Thread-2
Thread-0
Thread-1
尽管启动线程的顺序是thread1,thread2,thread3,但是被调用的顺序却不是按照启动顺序来的。即:调用start()方法的顺序不代表线程的启动顺序,线程的启动顺序具有不确定性。
2、run()方法
线程开始执行,虚拟机调用的是线程run()方法中的内容,当线程没有通过start()方法让其处于被调用状态的时候,其线程的run()方法不会被执行的。但是如果直接通过.run()来调用的话,还是可以调用的到,但这个时候run()方法所在线程就是主线程了,仅仅只是一个普通方法而已,不在充当线程中的run()方法的作用。
举例:通过.start()方法获取run()方法所在的线程
public class Thread01 implements Runnable{
@Override
public void run() {
System.out.println("run方法所在的线程为:" + Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
Runnable runnable = new Thread01();
//创建线程
Thread thread1 = new Thread(runnable);
thread1.start();
}
}
结果:
run方法所在的线程为:Thread-0
可以看到run()方法所在的线程就是我们创建的线程,而不再main主线程内。
通过.run()方法获取run()方法所在的线程
public class Thread01 implements Runnable{
@Override
public void run() {
System.out.println("run方法所在的线程为:" + Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
Runnable runnable = new Thread01();
//创建线程
Thread thread1 = new Thread(runnable);
thread1.run();
}
}
结果:
run方法所在的线程为:main
可以看到,此时run()方法所在的线程就是main主线程,而不再是我们自己创建的那个线程。
所以,通过start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。不通过start()方法来直接调用的话,run()方法只是thread的一个普通方法,还是在主线程里执行。
3、isAlive()方法
判断线程是否处于活动状态,只要线程处于启动且没有终止,返回的就是true。
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive();
举例:
public class Thread01 extends Thread{
@Override
public void run() {
System.out.println("线程run时的isAlive=====" + this.isAlive());
}
}
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
System.out.println("线程被new出来还未被调用时的isAlive======" + thread01.isAlive());
thread01.start();
System.out.println("线程通过start被调用时的isAlive======" + thread01.isAlive());
try {
thread01.sleep(1000);
System.out.println("线程执行完时的isAlive======" + thread01.isAlive());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
线程被new出来还未被调用时的isAlive======false
线程通过start被调用时的isAlive======true
线程run时的isAlive=====true
线程执行完时的isAlive======false
看到在start()之前,线程的isAlive是false,start()之后就是true了。main函数中加上Thread.sleep(100)的原因是为了确保Thread06的run()方法中的代码执行完,否则有可能end这里打印出来的是true,如下去掉sleep方法之后
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
System.out.println("线程被new出来还未被调用时的isAlive======" + thread01.isAlive());
thread01.start();
System.out.println("线程通过start被调用时的isAlive======" + thread01.isAlive());
System.out.println("线程执行完时的isAlive======" + thread01.isAlive());
}
}
结果:
线程被new出来还未被调用时的isAlive======false
线程通过start被调用时的isAlive======true
线程执行完时的isAlive======true
线程run时的isAlive=====true
产生这种结果的原因是main线程和thread01这两个线程并发执行,main线程执行完了的时候,thread01 线程还未开始执行,所以其isAlive的值是true。
4、getID()方法
这个方法比较简单,返回的就是当前线程对应的tid(Thread ID),这个tid是通过全局唯一的线程ID生成器threadSeqNumber来维护的,每new出一个线程threadSeqNumber都会自增一次,并赋予线程的tid属性,这个是Thread自己做的,用户无法自己制定tid。
举例:
public class Thread01 extends Thread{
@Override
public void run() {
System.out.println("线程run时的isAlive=====" + this.isAlive());
}
}
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
Thread01 thread02 = new Thread01();
System.out.println(thread01.getId());
System.out.println(thread02.getId());
}
}
结果:
11
12
5、getName()方法
new一个线程的时候,可以指定线程的名字,也可以不指定。如果指定,线程的名字就是我们指定的名字,getName()返回的就是我们指定的线程的名字。如果不指定,getName()返回的是"Thread-" + threadInitNumber,其中threadInitNumber是一个int型全局唯一的线程初始号生成器,threadInitNumber通过自增来维护线程初始号,所以返回的就是Thread-0,Thread-1,Thread-2等等。
举例:
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
Thread01 thread02 = new Thread01();
System.out.println(thread01.getName());
System.out.println(thread02.getName());
}
}
结果:
Thread-0
Thread-1
6、getPriority()和setPriority(int newPriority)
这两个方法用于获取和设置线程的优先级,优先级高的线程获取CPU的资源比较多,会比较容易先执行(不是一定会先执行)。线程的优先级别为1-10,其中1优先级最低,10优先级最高,即优先级越高的线程越先执行。
举例:未设置优先级时
public class Thread01 extends Thread{
@Override
public void run() {
for(int i = 0; i < 100000; i++) {
}
System.out.println("※※※" + "的优先级为:" + this.getPriority());
}
}
public class Thread02 extends Thread{
@Override
public void run() {
for(int i = 0; i < 100000; i++) {
}
System.out.println("======" + "的优先级为:" + this.getPriority());
}
}
public class Test {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
Thread01 thread01 = new Thread01();
Thread02 thread02 = new Thread02();
thread01.start();
thread02.start();
}
}
}
结果:
※※※的优先级为:5
======的优先级为:5
※※※的优先级为:5
======的优先级为:5
======的优先级为:5
※※※的优先级为:5
※※※的优先级为:5
======的优先级为:5
======的优先级为:5
※※※的优先级为:5
可以看到,未设置线程的优先级的时候,线程的优先级别默认是5,此时※与=代表的线程并没有明显的先后执行顺序,下面设置一下线程的优先级
public class Test {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
Thread01 thread01 = new Thread01();
Thread02 thread02 = new Thread02();
thread01.setPriority(10);
thread02.setPriority(1);
thread01.start();
thread02.start();
}
}
}
结果:
※※※的优先级为:10
※※※的优先级为:10
※※※的优先级为:10
======的优先级为:1
======的优先级为:1
※※※的优先级为:10
※※※的优先级为:10
======的优先级为:1
======的优先级为:1
======的优先级为:1
可以看到,优先级别为10的线程会比较容易先执行,但并不是优先级高的线程执行完了在执行优先级低的线程,而是在该时间片内,优先级高的线程会容易先执行。
7、isDaemon()方法和setDaemon()方法
daemon:[ˈdi:mən] 守护的意思。讲解两个方法前,首先要知道理解一个概念。Java中有两种线程,一种是用户线程(user thread),一种是守护线程(daemon thread),守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是GC线程。如果进程中不存在非守护线程(即用户线程)了,那么守护线程自动销毁,因为没有存在的必要,为别人服务,结果服务的对象都没了,当然就销毁了。
举例:thread01不设置成守护线程
public class Thread01 extends Thread{
private int i = 1;
@Override
public void run() {
while(true){
try {
Thread.sleep(200);
System.out.println(i);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
thread01.start();
System.out.println("thread01是否是守护线程==" + thread01.isDaemon());
System.out.println("main线程是否是守护线程==" + Thread.currentThread().isDaemon());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main主线程执行完了");
}
}
说明:此程序中存在两个线程,一个是main主线程(用户线程),一个是thread01(用户线程),两个线程并发执行,所以main线程执行完了之后并不影响thread01 线程的继续执行,因为thread01中的while一致为true,所以thread01会一直执行下去
结果:
thread01是否是守护线程==false
main线程是否是守护线程==false
1
2
3
4
main主线程执行完了
5
6
7
8
9
...
...
...
...
...
thread01设置成守护线程后
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
thread01.setDaemon(true);
thread01.start();
System.out.println("thread01是否是守护线程==" + thread01.isDaemon());
System.out.println("main线程是否是守护线程==" + Thread.currentThread().isDaemon());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main主线程执行完了");
}
}
结果:
thread01是否是守护线程==true
main线程是否是守护线程==false
1
2
3
4
main主线程执行完了
说明:将thread01线程设置成守护线程后,该程序中的两个线程(main线程和thread01线程)并发执行,但是当main线程执行完之后,作为守护线程的thread01自动销毁,不在继续执行。
关于守护线程,有一个细节注意下,setDaemon(true)必须在线程start()之前设置(This method must be invoked before the thread is started)。
8、interrupt()方法
这是一个有点误导性的名字,实际上Thread类的interrupt()方法无法中断线程。
举例:
public class Thread01 extends Thread{
@Override
public void run() {
for(int i = 0; i < 50000; i++) {
System.out.println("i = " + (i + 1));
}
}
}
public class Test {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
thread01.start();
thread01.interrupt();
}
}
结果:
.............
.............
i = 49997
i = 49998
i = 49999
i = 50000
看结果还是打印到了50000。也就是说,尽管调用了interrupt()方法,但是线程并没有停止。interrupt()方法的作用实际上是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞状态。换句话说,没有被阻塞的线程,调用interrupt()方法是不起作用的。关于这个会在之后讲中断机制的时候,专门写一篇文章讲解。
9、isInterrupted()方法
测试线程是否处于中断状态,但是不清除状态标识。(Tests whether this thread has been interrupted. The interrupted status of the thread is unaffected by this method)。这个和interrupt()方法一样,在后面讲中断机制的文章中专门会讲到。
10、join()方法
该方法暂时不写,后续会补上。
参考资料:
[Java] Thread的start()和run()函数区别
Java多线程3:Thread中的实例方法的更多相关文章
- java 多线程 2 Thread中start()和run()的区别
- Java多线程2:Thread中的实例方法
Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...
- 2.Thread中的实例方法
(转自:http://www.cnblogs.com/xrq730/p/4851233.html) Thread类中的方法调用方式: 1.this.XXX 这种调用方式表示的线程是:线程实例本身 2. ...
- java多线程创建-Thread,Runnable,callable和threadpool
java创建多线程的方式有许多种,这里简要做个梳理 1. 继承Thread类 继承java.lang.Thread类,创建本地多线程的类,重载run()方法,调用Thread的方法启动线程.示例代码如 ...
- Java多线程01(Thread类、线程创建、线程池)
Java多线程(Thread类.线程创建.线程池) 第一章 多线程 1.1 多线程介绍 1.1.1 基本概念 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于 ...
- JAVA多线程(一) Thread & Runnable
githut代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service/ ...
- Java 多线程(1)-Thread和Runnable
一提到Java多线程,首先想到的是Thread继承和Runnable的接口实现 Thread继承 public class MyThread extends Thread { public void ...
- Java 多线程之 Thread 类 和 Runnable 接口初步使用
目录 Thread 类 Thread之定义线程类 Thread之开启线程 Runnable 接口 Runnable 之定义线程类 Runnable 之开启线程 @ Thread 类 Thread 类是 ...
- Java 多线程查找文件中的内容
学过了操作系统,突然不知道多线程有什么用了. 看了一下百度,发现多线程,可以提升系统利用率 在系统进行IO操作的时候,CPU可以处理一些其他的东西,等IO读取到内存后,CPU再处理之前的操作. 总之可 ...
随机推荐
- Oracle调整顾问(SQL Tuning Advisor 与 SQL Access Advisor
在Oracle数据库出现性能问题时,使用Oracle本身的工具包,给出合理的调优建议是比较省力的做法. tuning advisor 是对输入的sql set的执行计划进行优化accsee advis ...
- 【转】FFmpeg 基本用法
FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...
- 巩固python基础
python是一门解释性的语言 变量命名规则 1.变量由字母,数字,下划线组成 2.不能用数字开头 3.禁止使用关键字 ‘and’,‘as’,‘assert’,‘break’,‘class’,‘con ...
- Java类文件结构详解
概述: Class文件结构是了解虚拟机的重要基础之一,如果想深入的了解虚拟机,Class文件结构是不能不了解的.Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在 ...
- 理解JSON Web Token (一)
一:理解单系统登录的原理及实现? web应用采用的 browser/server 架构的,http是无状态协议的,也就是说用户从A页面跳转到B页面会发起http请求,当服务器返回响应后,当用户A继续访 ...
- RabbitMQ详解(三)------RabbitMQ的五种队列
上一篇博客我们介绍了RabbitMQ消息通信中的一些基本概念,这篇博客我们介绍 RabbitMQ 的五种工作模式,这也是实际使用RabbitMQ需要重点关注的. 这里是RabbitMQ 官网中的相关介 ...
- C#理解AutoResetEvent和ManualResetEvent
当在C#使用多线程时就免不了使用AutoResetEvent和ManualResetEvent类,可以理解这两个类可以通过设置信号来让线程停下来或让线程重新启动,其实与操作系统里的信号量很相似(汗,考 ...
- 腾讯发布新版前端组件框架 Omi,全面拥抱 Web Components
Omi - 合一 下一代 Web 框架,去万物糟粕,合精华为一 → https://github.com/Tencent/omi 特性 4KB 的代码尺寸,比小更小 顺势而为,顺从浏览器的发展和 AP ...
- 牛客网 Python 编程输入规范
import sys try: while True: line = sys.stdin.readline().strip() if line == '': break lines = line.sp ...
- matplotlib 入门之Sample plots in Matplotlib
文章目录 Line Plot One figure, a set of subplots Image 展示图片 展示二元正态分布 A sample image Interpolating images ...