Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 240|回复: 0

[默认分类] Java多线程 5:Thread中的实例方法

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-4-26 11:04:01 | 显示全部楼层 |阅读模式
    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() 方法,产生一个异步执行的效果。有一点注意:调用 start() 方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性
    2、run()

    run() 方法是由线程调用 start() 方法之后,线程开始运行了,执行的方法。

    3、isAlive()
    isAlive() 方法的作用是测试线程是否处于活动状态
    看一个 Demo


    1. public class MyThread extends Thread { public void run() { System.out.println("run = " + this.isAlive()); // this 就表示的就是 mt 线程(注意和 Thread 的区别) } }
    复制代码



    1. public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); System.out.println("begin == " + mt.isAlive()); // 这里还没有启动,所以是 false mt.start();
    2. Thread.sleep(100);   System.out.println("end == " + mt.isAlive()); // main 线程睡了 100ms,mt 线程肯定执行完了,所以返回 false }
    复制代码


    运行结果

    1. begin == false run = true end == false
    复制代码


    根据结果我们可以得出结论:只要线程启动且没有终止,方法返回的就是 true
    4、getId()
    这个方法比较简单,就不写例子了。在一个 java 应用中,有一个long型的全局唯一的线程 ID 生成器 threadSeqNumber,每 new 出来一个线程都会把这个自增一次,并赋予线程的tid属性,这个是 Thread 自己做的,用户无法执行一个线程的 Id。
    5、getName()
    这个方法也比较简单,也不写例子了。我们 new 一个线程的时候,可以指定该线程的名字,也可以不指定。如果指定,那么线程的名字就是我们自己指定的,getName() 返回的也是开发者指定的线程的名字;如果不指定,那么 Thread 中有一个int型全局唯一的线程初始号生成器 threadInitNum,Java先把 threadInitNum 自增,然后以 "Thread-threadInitNum" 的方式来命名新生成的线程
    6、getPriority() 和 setPriority(int newPriority)
    这两个方法用于获取和设置线程的优先级,优先级高的 CPU 得到的 CPU 资源比较多,设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行。换句话说,两个在等待 CPU 的线程,优先级高的线程越容易被 CPU 选择执行。下面来看一下例子,并得出几个结论:

    1. public class MyThread_01 extends Thread { public void run() { System.out.println("MyThread_01 priority = " + this.getPriority()); } }
    复制代码



    1. public class MyThread_02 extends Thread { public void run() { System.out.println("MyThread_02 priority = " + this.getPriority()); MyThread_01 myThread_01 = new MyThread_01(); myThread_01.start(); } }
    复制代码



    1. public static void main(String[] args) { System.out.println("main thread, priority = " + Thread.currentThread().getPriority()); MyThread_02 myThread_02 = new MyThread_02(); myThread_02.start(); }
    复制代码


    运行结果

    1. main thread, priority = 5 MyThread_02 priority = 5 MyThread_01 priority = 5
    复制代码


    从这个例子我们得出结论:线程默认优先级为5,如果不手动指定,那么线程优先级具有继承性,比如线程A启动线程B,那么线程B的优先级和线程A的优先级相同
    下面的 Demo 演示了设置线程优先级带来的效果:

    1. package com.tkz; public class MyThread_01 extends Thread { public void run() { long beginTime = System.currentTimeMillis(); for (int j = 0; j < 100000; j++){} long endTime = System.currentTimeMillis(); System.out.println("◆◆◆◆◆ thread0 use time = " + (endTime - beginTime)); } public static void main(String[] args) { for (int i = 0; i < 5; i++) { MyThread_01 mt0 = new MyThread_01(); [b] mt0.setPriority([/b][b]10[/b][b]); // 在这里极端一点更能看出效果[/b] mt0.start(); MyThread_02 mt1 = new MyThread_02(); [b] mt1.setPriority([/b][b]1[/b][b]);[/b] mt1.start(); } } } class MyThread_02 extends Thread { public void run() { long beginTime = System.currentTimeMillis(); for (int j = 0; j < 100000; j++){} long endTime = System.currentTimeMillis(); System.out.println("◇◇◇◇◇ thread1 use time = " + (endTime - beginTime)); } }
    复制代码


    运行结果


    1. ◆◆◆◆◆ thread0 use time = 2 ◇◇◇◇◇ thread1 use time = 1 ◆◆◆◆◆ thread0 use time = 2 ◆◆◆◆◆ thread0 use time = 0 ◆◆◆◆◆ thread0 use time = 4 ◆◆◆◆◆ thread0 use time = 4 ◇◇◇◇◇ thread1 use time = 0 ◇◇◇◇◇ thread1 use time = 2 ◇◇◇◇◇ thread1 use time = 0 ◇◇◇◇◇ thread1 use time = 10
    复制代码


    从这个运行结果来看基本能得出结论:优先级越高的线程越能获取 CPU 资源
    7、isDaeMon、setDaemon(boolean on)
    讲解两个方法前,首先要知道理解一个概念。Java 中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是 GC 线程。如果进程中不存在非守护线程了,那么守护线程自动销毁,因为没有存在的必要,为别人服务,结果服务的对象都没了,当然就销毁了。理解了这个概念后,看一下例子:

    1. public class MyThread extends Thread { private int i = 0; public void run() { try { while (true) { i++; System.out.println("i = " + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { try { MyThread mt = new MyThread(); mt.setDaemon(true); mt.start(); Thread.sleep(5000); // main 线程在这里睡了 5 秒,那么 mt 作为守护线程在这 5s 内做了哪些事
    2. System.out.println("我离开thread对象再也不打印了,我停止了!"); } catch (InterruptedException e) { e.printStackTrace(); } } }
    复制代码


    运行结果

    1. i = 1
    2. i = 2
    3. i = 3
    4. i = 4
    5. i = 5
    6. main 线程即将运行结束了
    复制代码


    8、interrupt()
    这是一个有点误导性的名字,实际上 Thread 类的 interrupt() 方法无法中断线程。看一下例子:

    1. public class TestThreadInterupt { public static void main(String[] args) { try { MyThread mt = new MyThread(); mt.start(); Thread.sleep(1000); // main 线程睡了 2s
    2. mt.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 500000; i++) { System.out.println("i = " + (i + 1)); } } }
    复制代码


    运行结果

    1. ... i = 499995 i = 499996 i = 499997 i = 499998 i = 499999 i = 500000
    复制代码


    看结果还是打印到了 50000。也就是说,尽管调用了 interrupt() 方法,但是线程并没有停止。interrupt() 方法的作用实际上是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞状态。换句话说,没有被阻塞的线程,调用interrupt() 方法是不起作用的。关于这个会在之后讲中断机制的时候,专门写一篇文章讲解。
    9、isInterrupted()
    测试线程是否已经中断,但不清除状态标识。这个和 interrupt() 方法一样,在后面讲中断机制的文章中专门会讲到。
    10、join()
    讲解 join()方法之前要对 wait()/notify()/notifyAll() 机制已熟练掌握。
    join( )方法的作用是等待线程销毁。join() 方法反应的是一个很现实的问题,比如 main 线程的执行时间是1s,子线程的执行时间是10s,但是主线程依赖子线程执行完的结果,这时怎么办?可以像生产者/消费者模型一样,搞一个缓冲区,子线程执行完把数据放在缓冲区中,通知 main 线程,main 线程去拿,这样就不会浪费 main 线程的时间了。另外一种方法,就是 join() 了。
    看一个 Demo

    1. public class TestThreadInterupt { public static void main(String[] args) throws Exception { MyThread mt = new MyThread(); mt.start(); mt.join(); System.out.println("我是 "+Thread.currentThread().getName()+" 线程,当 mt 线程执行完毕之后我再执行"); } } class MyThread extends Thread { public void run() { try { int secondValue = (int)(Math.random() * 10000); System.out.println(secondValue); Thread.sleep(secondValue); } catch (InterruptedException e) { e.printStackTrace(); } } }
    复制代码


    运行结果

    1. 8241 我是 main 线程,当 mt 线程执行完毕之后我再执行
    复制代码


    join() 方法会使调用 join() 方法的线程(也就是 mt 线程)所在的线程(也就是 main 线程)无限阻塞,直到调用 join() 方法的线程销毁为止,此例中 main 线程就会无限期阻塞直到 mt 的 run() 方法执行完毕。
    join() 方法的一个重点是要区分出和 sleep() 方法的区别。join(2000) 也是可以的,表示调用 join() 方法所在的线程最多等待 2000ms,两者的区别在于:
    sleep(2000) 不释放锁,join(2000) 释放锁,因为 join() 方法内部使用的是 wait(),因此会释放锁。看一下 join(2000) 的源码就知道了,join() 其实和 join(2000) 一样,无非是 join(0) 而已:

    1. public final synchronized void join(long millis) throws InterruptedException {
    2. long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { [b]wait([/b][b]0[/b][b]);[/b] } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } [b]wait(delay);[/b] now = System.currentTimeMillis() - base; } } }
    复制代码


      
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-16 12:03 , Processed in 0.386113 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表