前言

昨天已经写了:

如果没看的同学建议先去阅读一遍哦~

在写文章之前通读了一遍《Java 核心技术 卷一》的并发章节和《Java并发编程实战》前面的部分,回顾了一下以前写过的笔记。从今天开始进入多线程的知识点咯~

我其实也是相当于从零开始学多线程的,如果文章有错的地方还请大家多多包含,不吝在评论区下指正呢~~

一、Thread线程类API

声明本文使用的是JDK1.8

实现多线程从本质上都是由Thread类来进行操作的~我们来看看Thread类一些重要的知识点。Thread这个类很大,不可能整个把它看下来,只能看一些常见的、重要的方法

顶部注释的我们已经解析过了,如果不知道的同学可前往:多线程三分钟就可以入个门了!

1.1设置线程名

我们在使用多线程的时候,想要查看线程名是很简单的,调用Thread.currentThread().getName()即可。

如果没有做什么的设置,我们会发现线程的名字是这样子的:主线程叫做main,其他线程是Thread-x

下面我就带着大家来看看它是怎么命名的:

nextThreadNum()的方法实现是这样的:

基于这么一个变量-->线程初始化的数量

点进去看到init方法就可以确定了:

看到这里,如果我们想要为线程起个名字,那也是很简单的。Thread给我们提供了构造方法

下面我们来测试一下:

  • 实现了Runnable的方式来实现多线程:

public class MyThread implements Runnable { @Override
public void run() {
// 打印出当前线程的名字
System.out.println(Thread.currentThread().getName());
}
}

测试:


public class MyThreadDemo {
public static void main(String[] args) { MyThread myThread = new MyThread(); //带参构造方法给线程起名字
Thread thread1 = new Thread(myThread, "关注公众号Java3y");
Thread thread2 = new Thread(myThread, "qq群:742919422"); thread1.start();
thread2.start(); // 打印当前线程的名字
System.out.println(Thread.currentThread().getName());
}
}

结果:

当然了,我们还可以通过setName(String name)的方法来改掉线程的名字的。我们来看看方法实现;

检查是否有权限修改:

至于threadStatus这个状态属性,貌似没发现他会在哪里修改

1.2守护线程

守护线程是为其他线程服务的

  • 垃圾回收线程就是守护线程~

守护线程有一个特点

  • 当别的用户线程执行完了,虚拟机就会退出,守护线程也就会被停止掉了。
  • 也就是说:守护线程作为一个服务线程,没有服务对象就没有必要继续运行

使用线程的时候要注意的地方

  1. 在线程启动前设置为守护线程,方法是setDaemon(boolean on)
  2. 使用守护线程不要访问共享资源(数据库、文件等),因为它可能会在任何时候就挂掉了。
  3. 守护线程中产生的新线程也是守护线程

测试一波:


public class MyThreadDemo {
public static void main(String[] args) { MyThread myThread = new MyThread(); //带参构造方法给线程起名字
Thread thread1 = new Thread(myThread, "关注公众号Java3y");
Thread thread2 = new Thread(myThread, "qq群:742919422"); // 设置为守护线程
thread2.setDaemon(true); thread1.start();
thread2.start();
System.out.println(Thread.currentThread().getName());
}
}

上面的代码运行多次可以出现(电脑性能足够好的同学可能测试不出来):线程1和主线程执行完了,我们的守护线程就不执行了~

原理:这也就为什么我们要在启动之前设置守护线程了。

1.3优先级线程

线程优先级高仅仅表示线程获取的CPU时间片的几率高,但这不是一个确定的因素

线程的优先级是高度依赖于操作系统的,Windows和Linux就有所区别(Linux下优先级可能就被忽略了)~

可以看到的是,Java提供的优先级默认是5,最低是1,最高是10:

实现:

setPriority0是一个本地(navite)的方法:


private native void setPriority0(int newPriority);

1.4线程生命周期

在上一篇介绍的时候其实也提过了线程的线程有3个基本状态:执行、就绪、阻塞

在Java中我们就有了这个图,Thread上很多的方法都是用来切换线程的状态的,这一部分是重点!

其实上面这个图是不够完整的,省略掉了一些东西。后面在讲解的线程状态的时候我会重新画一个~

下面就来讲解与线程生命周期相关的方法~

1.4.1sleep方法

调用sleep方法会进入计时等待状态,等时间到了,进入的是就绪状态而并非是运行状态

于是乎,我们的图就可以补充成这样:

1.4.2yield方法

调用yield方法会先让别的线程执行,但是不确保真正让出

  • 意思是:我有空,可以的话,让你们先执行

于是乎,我们的图就可以补充成这样:

1.4.3join方法

调用join方法,会等待该线程执行完毕后才执行别的线程~

我们进去看看具体的实现

wait方法是在Object上定义的,它是native本地方法,所以就看不了了:

wait方法实际上它也是**计时等待(如果带时间参数)**的一种!,于是我们可以补充我们的图:

1.4.3interrupt方法

线程中断在之前的版本有stop方法,但是被设置过时了。现在已经没有强制线程终止的方法了!

由于stop方法可以让一个线程A终止掉另一个线程B

  • 被终止的线程B会立即释放锁,这可能会让对象处于不一致的状态
  • 线程A也不知道线程B什么时候能够被终止掉,万一线程B还处理运行计算阶段,线程A调用stop方法将线程B终止,那就很无辜了~

总而言之,Stop方法太暴力了,不安全,所以被设置过时了。

我们一般使用的是interrupt来请求终止线程~

  • 要注意的是:interrupt不会真正停止一个线程,它仅仅是给这个线程发了一个信号告诉它,它应该要结束了(明白这一点非常重要!)
  • 也就是说:Java设计者实际上是想线程自己来终止,通过上面的信号,就可以判断处理什么业务了。
  • 具体到底中断还是继续运行,应该由被通知的线程自己处理

Thread t1 = new Thread( new Runnable(){
public void run(){
// 若未发生中断,就正常执行任务
while(!Thread.currentThread.isInterrupted()){
// 正常任务代码……
}
// 中断的处理代码……
doSomething();
}
} ).start();

再次说明:调用interrupt()并不是要真正终止掉当前线程,仅仅是设置了一个中断标志。这个中断标志可以给我们用来判断什么时候该干什么活!什么时候中断由我们自己来决定,这样就可以安全地终止线程了!

我们来看看源码是怎么讲的吧:

再来看看刚才说抛出的异常是什么东东吧:

所以说:interrupt方法压根是不会对线程的状态造成影响的,它仅仅设置一个标志位罢了

interrupt线程中断还有另外两个方法(检查该线程是否被中断)

  • 静态方法interrupted()-->会清除中断标志位
  • 实例方法isInterrupted()-->不会清除中断标志位

上面还提到了,如果阻塞线程调用了interrupt()方法,那么会抛出异常,设置标志位为false,同时该线程会退出阻塞的。我们来测试一波:



public class Main {
/**
* @param args
*/
public static void main(String[] args) {
Main main = new Main(); // 创建线程并启动
Thread t = new Thread(main.runnable);
System.out.println("This is main ");
t.start(); try { // 在 main线程睡个3秒钟
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("In main");
e.printStackTrace();
} // 设置中断
t.interrupt();
} Runnable runnable = () -> {
int i = 0;
try {
while (i < 1000) { // 睡个半秒钟我们再执行
Thread.sleep(500); System.out.println(i++);
}
} catch (InterruptedException e) { // 判断该阻塞线程是否还在
System.out.println(Thread.currentThread().isAlive()); // 判断该线程的中断标志位状态
System.out.println(Thread.currentThread().isInterrupted()); System.out.println("In Runnable");
e.printStackTrace();
}
};
}

结果:

接下来我们分析它的执行流程是怎么样的:

2018年4月18日20:32:15(哇,这个方法真的消耗了我非常长的时间).....感谢@开始de痕迹的指教~~~~~

该参考资料:

二、总结

可以发现我们的图是还没有补全的~后续的文章讲到同步的时候会继续使用上面的图的。在Thread中重要的还是那几个可以切换线程状态的方法,还有理解中断的真正含义。

使用线程会导致我们数据不安全,甚至程序无法运行的情况的,这些问题都会再后面讲解到的~

之前在学习操作系统的时候根据《计算机操作系统-汤小丹》这本书也做了一点点笔记,都是比较浅显的知识点。或许对大家有帮助

参考资料:

  • 《Java核心技术卷一》
  • 《Java并发编程实战》
  • 《计算机操作系统-汤小丹》

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航

Thread源码剖析的更多相关文章

  1. Java中Thread源码剖析

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 关于线程,用很长时间了,主线程下的子线程去做一些事情,就是一个代理模式,主线程分代理权给子线程,子线 ...

  2. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

  3. 自己实现多线程的socket,socketserver源码剖析

    1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...

  4. Java多线程9:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

  5. socket_server源码剖析、python作用域、IO多路复用

    本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ...

  6. 玩转Android之Picasso使用详详详详详详解,从入门到源码剖析!!!!

    Picasso是Squareup公司出的一款图片加载框架,能够解决我们在Android开发中加载图片时遇到的诸多问题,比如OOM,图片错位等,问题主要集中在加载图片列表时,因为单张图片加载谁都会写.如 ...

  7. jdk源码剖析:Synchronized

    开启正文之前,先说一下源码剖析这一系列,就以"死磕到底"的精神贯彻始终,最少追踪到JVM指令(再往下C语言实现了). =========正文分割线===========  Sync ...

  8. 从Thread.start()方法看Thread源码,多次start一个线程会怎么样

    这篇文章作为Thread类源码剖析的补充,从一个侧面来看Thread源码.也解答了面试高频问题:"多次start一个线程会怎么样?" 答案是:java.lang.IllegalTh ...

  9. ThreadLocal终极源码剖析

    目录一.ThreadLocal1.1 源码注释1.2 源码剖析      散列算法-魔数0x61c88647      set操作    get操作    remove操作1.3 功能测试1.4 应用 ...

随机推荐

  1. jdk 环境配置踩坑

    其实在网上已经有很多环境配置的介绍了.不过我还是想用切身经历告诉大家这里面可能遇到的坑. 首先,先给大家讲一下JAVA_HOME,path,CLASSPATH JAVA_HOME 指向的是JDK的安装 ...

  2. python多进程并发redis

    Redis支持两种持久化方式RDB和AOF,RDB持久化能够快速的储存和回复数据,但在服务器停机时会丢失大量数据,AOF持久化能够高效的提高数据的安全性,但在储存和恢复数据方面要耗费大量的时间,最好的 ...

  3. Redis——常见面试题

    一.memcached与redis的区别? 1.存储方式不同.memcached把数据全部存在内存之中,断电之后会挂掉,而redis虽然也用到了内存,但是会有部分数据存在硬盘中,保证数据持久性. 2. ...

  4. Beta 第二天

    今天遇到的困难: 组员对github极度的不适应 Android Studio版本不一致项目难以打开运行 移植云端的时候,愚蠢的把所有项目开发环境全部搬上去.本身云的内存小,性能差,我们花费了太多时间 ...

  5. Beta冲刺第六天

    一.昨天的困难 没有困难. 二.今天进度 1.林洋洋:更新申请ip为域名,去除druid数据源统计 2.黄腾达:协作详情中添加成员对话框优化 3.张合胜:修复侧栏菜单mini状态下不能显示问题 三.明 ...

  6. 关于python词典(Dictionary)的get()用法

    先贴出参考链接:http://www.runoob.com/python/att-dictionary-get.html get()方法语法: dict.get(key, default=None) ...

  7. 个人作业2:QQ音乐APP案例分析

    APP案例分析 QQ音乐 选择理由:毕竟作为QQ音乐九年的资深老用户以及音乐爱好者 第一部分 调研 1.第一次上手的体验    我算是很早期的QQ音乐的用户,用QQ音乐七八年,除了体验各方面还不错之外 ...

  8. 2018C程序设计—第0次作业

    1.翻阅邹欣老师博客关于师生关系博客,并回答下列问题,每个问题的答案不少于500字 1)最理想的师生关系是健身教练和学员的关系,在这种师生关系中你期望获得来自老师的哪些帮助? 答:正如邹欣老师博客里所 ...

  9. 2017-2018-1 我爱学Java 第二周 作业

    Android Game Discussion Questions Answers 20162309邢天岳 20162311张之睿 20162312张家铖 20162313苑洪铭 20162324春旺 ...

  10. NetFPGA-1G-CML点亮 LED

    前言 用vivado建立工程的时候选择的型号为:XC7K325tffg676-1 在以下代码文件中,仿真与设计都没有问题.在xdc文件中的时钟约束与锁相环配置中还存在问题,没有寻找到解决办法 使用手册 ...