Java线程核心基础(上)

一、实现多线程

  根据Oracle官方文档,目前推荐的创建线程方法主要有两种,分别是继承Thread类和实现Runnable接口。通过阅读Thread类源码,可以发现继承Thread类需要重写run()方法,而实现Runnable接口会将自己实现的对象在new Thread()时,通过Thread构造函数传给Thread类中的target对象,并在调用run()方法时调用target.run(),下面让我们看源码。

/*
What will be run. 这是Thread类中的target对象
*/
private Runnable target;
/*
当调用run()方法时会判断target是否为空,
如果是继承Thread类run()方法被重写,就不会执行以下代码了
*/
 @Override
public void run() {
if (target != null) {
target.run();
}
}

  那么这两种方法哪一个更好呢? 实现Runnable接口更好,由于Java是单继承类但可以实现多个接口,如果继承了Thread类后续由于业务需要就不能继承新的类了,而实现Runable接口就没有这个问题。另外对于线程池,Callable,FutureTask,定时器,匿名内部类,lambda表达式等其他可以创建线程的方法,究其本质只是对以上两种方法进行了包装。

  如果同时实现了两种方法会发生什么?即既传入Ruable对象,又重写run()方法。答案是会调用重写的run()方法,根据面向对象思想,子类重写父类方法,则父类原方法就无法调用了,target.run()也就无法执行了。

  最后对这两种实现线程的方式一句话总结:一种创建线程的方式,两种实现执行单元的方式。

二、start() 和 run()方法的比较

  start()方法可以启动新线程,并做准备工作,start()方法不能重复调用,会在第二次调用时抛出IIegalThreadStateException()。下面看一下源码

 

// 线程状态默认未启动
private volatile int threadStatus = 0; public synchronized void start() {
// 判断线程是否已启动,已启动则抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 加入线程组
group.add(this); boolean started = false;
try {
// 调用native方法创建线程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}

  run()方法直接调用就是普通方法,不会创建新线程执行,只用使用start()方法间接调用run()方法才能在新线程中执行。

三、如何正确停止线程

  这个内容非常重要,停止线程应使用interrupt来通知,而不知强制停止。如何使用interrupt来请求停止线程呢? 有以下几种情况:

    1. 普通情况run() 方法中没有sleep() 或wait()方法时,可以使用isInterrupted()方法进行判断。 ( 关于isInterrupted() 和 inInterrupted() 的区别后面会讲到)

    2. 线程可能阻塞的情况, 当线程阻塞时收到interrupt中断会立即抛出异常响应中断,线程结束

    3. 如果线程在每次工作迭代之后都阻塞,可以在迭代外层try/catch捕获异常并中断线程,如果在迭代内try/catch捕获异常,线程无法停止,因为sleep()或wait()方法会把interrupt标记位清除。

  在实际生产开发过程中,对于停止线程的最佳的处理方式:

    1. 优先选择: 传递中断

    2. 不想或无法传递:恢复中断

    3.不应屏蔽中断

  错误的处理方式:在方法中吞掉中断。 可将异常抛到顶层在run()方法中处理。

  另外,错误停止线程的方法

  1. 被弃用的stop(), suspend()和resume() 方法, 使用stop()会使线程戛然而止,导致线程不能进行最后的收尾工作,可能对系统造成损害。 suspend()会挂起线程但是不会释放锁,可能会造成死锁。

  2. 使用volatile设置Boolean标记位,这个方法相信很多人都会怀疑,啊?这个也是错误的?其实这个方法错就错在,虽然volatile能保证标记位对于线程随时可见,但是当线程阻塞时,是无法检查标记位的,如果没有其它线程唤醒,则阻塞线程会进入永久阻塞。 正确方法还是用interrupt()来通知要停止的线程。

四、线程的生命周期

  线程总共有六个状态,New 已创建但还尚未启动的新线程,Runnable可运行,Blocked被阻塞,Waiting等待,Timed Waiting限期等待,Terminated终止。 一般而言会把 Blocked,Waiting,Timed Waiting都称为阻塞状态。 

     

Java线程核心基础(上)的更多相关文章

  1. Java线程池基础

    目录: 一.线程池概述 二.线程池参数 三.线程池的执行过程 四.线程池的主要实现 五.线程池的使用 六.线程池的正确关闭方式 七.线程池参数调优 一.线程池概述 1.线程池类 目前线程池类一般有两个 ...

  2. Java线程并发控制基础知识

    微博上众神推荐今年4月刚刚出版的一本书,淘宝华黎撰写的<大型网站系统与Java中间件实践>,一线工程师的作品,实践出真知,果断要看. 前两章与<淘宝技术这十年>内容类似,基本是 ...

  3. java面试核心基础(1)

    1.以下代码的执行结果 String s1 = "helloworld"; String s2 = "hello" + new Stirng("wor ...

  4. Java线程之基础

    Java内存模型(jmm) 线程通信 消息传递 重排序 顺序一致性 Happens-Before As-If-Serial 一.线程的生命周期及五种基本状态 线程生命周期:新建.就绪.运行.阻塞.死亡 ...

  5. java线程锁基础

    定义运行方法 package com.company; // 包名import java.util.concurrent.locks.ReentrantLock;import java.util.co ...

  6. java线程基础知识----线程与锁

    我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...

  7. Java线程池ThreadPoolExector的源码分析

    前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等 ...

  8. 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型

    关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...

  9. Java线程和多线程(十三)——Callable,Future,FutureTask

    在Java多线程之中,Callable和Future的使用时非常广泛的.在之前的文章中,我们了解了关于Java线程池基础的一些内容,知道如何提交Runnable的任务.但是,Runnable的任务是无 ...

随机推荐

  1. Ceph 文件系统-全网最炫酷的Ceph Dashboard页面和Ceph监控 -- <5>

    Ceph Dashboard实现 Ceph Dashboard介绍 Ceph 的监控可视化界面方案很多----grafana.Kraken.但是从Luminous开始,Ceph 提供了原生的Dashb ...

  2. ACM北大暑期课培训第二天

    今天继续讲的动态规划 ... 补充几个要点: 1. 善于利用滚动数组(可减少内存,用法与计算方向有关) 2.升维 3.可利用一些数据结构等方法使代码更优  (比如优先队列) 4.一般看到数值小的 (十 ...

  3. 【转】Java 面试题问与答:编译时与运行时

    在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念.理解这几个概念可以更好地帮助你去了解一些基本的原理.下面是初学者晋级中级水平需要知道的一些问题. Q.下面的代码片段中,行A和行B所 ...

  4. php代码没解析成功

    在Apache中加载PHP模块 1.打开Apache的配置文件httpd.conf(位于Apache2\conf 目录下). 2.查找 “#LoadModule ssl_module modules/ ...

  5. 2017-10-28 noip模拟赛by WISCO 信息组

    第一次做模拟赛,自我感觉良好(大概是这套题比较简单) T1 名称为“数据结构”,这也太坑了点……233 要维护一个数列(初始为零),支持区间加与查询. 查询的是一个区间中有多少数满足min<=( ...

  6. 可视化限流管理,Sentinel 控制台启动和接入

    Sentinel 的使用可以分为核心库和控制台两个部分. 核心库不依赖任何框架/库,集成了主流框架,可以进行单机限流降级等功能, 控制台Dashboard提供了可视化的管理限流规则.对集群进行监控,集 ...

  7. BZOJ4559 成绩比较

    题目传送门 分析: 我们可以先试着求一下,对于单个学科,有多少种分配方案可以使B神排名为R 对于第i个学科 \(~~~~g(i)=\sum_{j=1}^{H_i}j^{n-R_i}(H_i-j)^{R ...

  8. NOI4.6 最小新整数——切山游戏

    描述 给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0.n的位数为m. 现在从m位中删除k位(0<k < m),求生成的新整数最小为多少? ...

  9. android开发实战-记账本APP(一)

    记账本开发流程: 对于一个记账本的初步开发而言,我实现的功能有: ①实现一个记账本的页面 ②可以添加数据并更新到页面中 ③可以将数据信息以图表的形式展现 (一)首先,制作一个记账本的页面. ①在系统自 ...

  10. Docker基础内容之容器

    前言 容器是独立运行的一个或一组应用以及它们的运行态环境. 相关命令 启动容器相关命令 docker run 运行一个ubuntu14.04版本的容器,如果这个镜像本地不存在则会去默认仓库中下载 do ...