Java核心(一)线程Thread详解
一、概述
在开始学习Thread之前,我们先来了解一下 线程和进程之间的关系:
线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。
由上描述,可以得知线程作为cpu的基本调度单位,只有把多线程用好,才能充分利用cpu的多核资源。
本文基于JDK 8(也可以叫JDK 1.8)。
二、线程使用
2.1 启动线程
创建线程有四种方式:
- 实现Runnable接口
- 继承Thread类
- 使用JDK 8 的Lambda
- 使用Callable和Future
2.1.1 Runnable创建方式
public class MyThread implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
Thread thread = new Thread(new MyThread());
thread.start();
2.1.2 继承Thread创建方式
public class MyThread extends Thread{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
MyThread thread = new MyThread();
thread.start();
以上代码有更简单的写法,如下:
Thread thread = new Thread(){@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
};
thread.start();
2.1.3 Lambda创建方式
new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
2.1.4 使用Callable和Future
看源码可以知道Thread的父类是Runnable是JDK1.0提供的,而Callable和Runnable类似,是JDK1.5提供的,弥补了调用线程没有返回值的情况,可以看做是Runnable的一个补充,下面看看Callable的实现。
public class MyThread implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName());return Thread.currentThread().getName();}
}
Callable<String> callable = new MyThread();
FutureTask<String> ft = new FutureTask<>(callable);
new Thread(ft,"threadName").start();
System.out.println(ft.get());
2.1.5 run()和start()的区别
真正启动线程的是start()方法而不是run(),run()和普通的成员方法一样,可以重复使用,但不能启动一个新线程。
2.2 Thread的常用方法
Thread类方法
方法 | 说明 |
---|---|
start() | 启动线程 |
setName(String name) | 设置线程名称 |
setPriority(int priority) | 设置线程优先级,默认5,取值1-10 |
join(long millisec) | 挂起线程xx毫秒,参数可以不传 |
interrupt() | 终止线程 |
isAlive() | 测试线程是否处于活动状态 |
Thread静态(static)方法
方法 | 说明 |
---|---|
yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
sleep(long millisec)/sleep(long millis, int nanos) | 挂起线程xx秒,参数不可省略 |
currentThread() | 返回对当前正在执行的线程对象的引用 |
holdsLock(Object x) | 当前线程是否拥有锁 |
2.3 sleep()和wait()的区别
sleep为线程的方法,而wait为Object的方法,他们的功能相似,最大本质的区别是:sleep不释放锁,wait释放锁。
用法上的不同:sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来终止线程;wait()可以用notify()/notifyAll()直接唤起。
重点: 测试wait和sleep释放锁的代码如下:
public class SynchronizedTest extends Thread {int number = 10;public synchronized void first(){System.out.println("this is first!");number = number+1;}public synchronized void secord() throws InterruptedException {System.out.println("this is secord!!");Thread.sleep(1000);
// this.wait(1000);number = number*100;}@Overridepublic void run() {first();}
}
SynchronizedTest synchronizedTest = new SynchronizedTest();
synchronizedTest.start();
synchronizedTest.secord();
// 主线程稍等10毫秒
Thread.sleep(10);
System.out.println(synchronizedTest.number);
根据结果可以得知:
- 执行sleep(1000)运行的结果是:1001
- 执行wait(1000)运行的结果是:1100
总结: 使用 sleep(1000)不释放同步锁,执行的是10*100+1=1001,wait(1000)释放了锁,执行的顺序是(10+1)x100=1100,所以sleep不释放锁,wait释放锁。
三、线程状态
3.1 线程状态概览
线程状态:
- NEW 尚未启动
- RUNNABLE 正在执行中
- BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
- WAITING 永久等待状态
- TIMED_WAITING 等待指定的时间重新被唤醒的状态
- TERMINATED 执行完成
线程的状态可以使用getState()查看,更多状态详情,查看Thread源码,如下图:
3.2 线程的状态代码实现
3.2.1 NEW 尚未启动状态
Thread thread = new Thread() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
};
// 只声明不调用start()方法,得到的状态是NEW
System.out.println(thread.getState()); // NEW
3.2.2 RUNNABLE 运行状态
Thread thread = new Thread() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
};
thread.start();
System.out.println(thread.getState()); // RUNNABLE
3.2.3 BLOCKED 阻塞状态
使用synchronized同步阻塞实现,代码如下:
public class MyCounter {int counter;public synchronized void increase() {counter++;try {Thread.sleep(10*1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
MyCounter myCounter = new MyCounter();
// 线程1调用同步线程,模拟阻塞
new Thread(()-> myCounter.increase()).start();
// 线程2继续调用同步阻塞方法
Thread thread = new Thread(()-> myCounter.increase());
thread.start();// 让主线程等10毫秒
Thread.currentThread().sleep(10);
// 打印线程2,为阻塞状态:BLOCKED
System.out.println(thread.getState());
3.2.4 WAITING 永久等待状态
public class MyThread extends Thread{@Overridepublic void run() {synchronized (MyThread.class){try {MyThread.class.wait();System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}
}
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出WAITING,线程thread一直处于被挂起状态
System.out.println(thread.getState());
唤醒线程: 可使用 notify/notifyAll 方法,代码如下:
synchronized (MyThread.class) {MyThread.class.notify();
}
使线程WAITING的方法:
- Object的wait() 不设置超时时间
- Thread.join()不设置超时时间
- LockSupport的park()
查看Thread源码可以知道Thread的join方法,底层使用的是Object的wait实现的,如下图:
注意: 查看Object的源码可知wait(),不传递参数,等同于wait(0),设置的“0”不是立即执行,而是无限的等待,不执行,如下图:
3.2.5 TIMED_WAITING 超时等待状态
TIMED_WAITING状态,只需要给wait设置上时间即可,代码如下:
public class MyThread extends Thread{@Overridepublic void run() {synchronized (MyThread.class){try {MyThread.class.wait(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}
}
调用代码还是一样的,如下:
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出TIMED_WAITING
System.out.println(thread.getState());
synchronized (MyThread.class) {MyThread.class.notify();
}
3.2.6 TERMINATED 完成状态
Thread thread = new Thread(()-> System.out.println(Thread.currentThread().getName()));
thread.start();
// 让主线程等10毫秒
Thread.currentThread().sleep(10);
System.out.println(thread.getState());
四、死锁
根据前面的知识,我们知道使用sleep的时候是不释放锁的,所以利用这个特性我们可以很轻易的写出死锁的代码,具体的流程如图(图片来源于杨晓峰老师文章):
代码如下:
static Object object1 = new Object();
static Object object2 = new Object();public static void main(String[] args) {Thread thread = new Thread(){@Overridepublic void run() {synchronized (object1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (object2){System.out.println(Thread.currentThread().getName());}}}};Thread thread2 = new Thread(){@Overridepublic void run() {synchronized (object2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (object1){System.out.println(Thread.currentThread().getName());}}}};thread.start();thread2.start();
运行上面的代码,程序会处于无限等待之中。
五、总结
根据上面的内容,我们已经系统的学习Thread的使用了,然而学而不思则罔,最后留一个思考题:根据本文介绍的知识,怎么能避免死锁?
源码下载:https://github.com/vipstone/java-core-example.git
参考文档
Java核心技术36讲:http://t.cn/EwUJvWA
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html
Java核心(一)线程Thread详解相关推荐
- Java多线程之线程池详解
Java多线程之线程池详解 目录: 线程池使用及优势 线程池3个常用方式 线程池7大参数深入介绍 线程池底层工作原理 1. 线程池使用及优势 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务 ...
- python 线程thread详解
join详解 看到代码示例时,都会出现一个join,这个作用如何?先看结论 阻塞主程序,专注于执行多线程中的程序 多线程多join的情况下,依次执行各线程的join方法,子线程全部结束了才能执行主线程 ...
- 【java】java多线程及线程池详解
目录 前言 线程是什么?多线程是什么? 多线程的作用和好处以及缺点 守护线程和用户线程 并发和并行的区别 一.线程的状态和常用方法 1.线程各种状态转化图 2.线程相关常用方法有 ① wait() ② ...
- Java笔记之线程池详解
文章目录 一.线程池是什么? 二.为什么要使用线程池? 三.jdk自带的四种线程池 1. 线程池参数 2.工作队列 3.拒绝策略 4.四种线程池一些示例 四.自定义线程池 一.线程池是什么? 一种线程 ...
- Java并发:线程池详解(ThreadPoolExecutor)
前言 现在在实现异步时,基本都是使用线程池来实现,线程池在工作应用的还是比较频繁的,本文将就线程池的使用.相关原理和主要方法源码进行深入讲解学习. 线程池的基本使用 package com.joonw ...
- java线程池详解及五种线程池方法详解
基础知识 Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThrea ...
- Java线程池详解学习:ThreadPoolExecutor
Java线程池详解学习:ThreadPoolExecutor Java的源码下载参考这篇文章:Java源码下载和阅读(JDK1.8) - zhangpeterx的博客 在源码的目录java/util/ ...
- Java 线程池详解及实例代码
转载自 Java 线程池详解及实例代码 这篇文章主要介绍了Java 线程池的相关资料,并符实例代码,帮助大家学习参考,需要的朋友可以参考下 线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时 ...
- java闭合数据_java多线程中线程封闭详解
线程封闭的概念 访问共享变量时,通常要使用同步,所以避免使用同步的方法就是减少共享数据的使用,这种技术就是线程封闭. 实现线程封闭的方法 1:ad-hoc线程封闭 这是完全靠实现者控制的线程封闭,他的 ...
最新文章
- oracle 统计信息字典表,Oracle数据字典表查询
- 留学申请计算机硕士个人陈述,申请美国计算机专业英文个人陈述
- ViewTreeObserver视图树观察者
- Python初学者之ImportError: No module named moviepy.editor 的解决办法
- 返回包禁止返回server_kubernetes部署metrics-server
- 7-37 模拟EXCEL排序 (25 分)(思路+详解+超时解决 兄弟们冲呀呀呀呀呀呀)
- 其他一些单元测试技巧
- wince中的BSP工程的相关文件介绍
- vshost32.exe停止工作
- 生意做到一定规模,老板想面面俱到,亲力亲为就不可能了
- 网页传上服务器 是乱码,前端传到后台中文乱码问题
- [Head First设计模式]云南米线馆中的设计模式——模版方法模式
- Ubuntu 11.10为何值得我们期待?
- 程序员,小红书王牌生活记录家。这是一篇记录程序员生涯的笔记。
- 基于RAM的雷达线性调频信号产生
- Sketch for Mac汉化破解教程含汉化包
- iconfont阿里巴巴矢量图标引入方法
- QT多线程,使用串口接收数据通过UDP端口进行数据转发
- eclipse使用maven新建类目录时,提示The folder is already a source folder
- Rocky Linux Yum源替换位上海交大镜像站点
热门文章
- Redis 核心技术与实战
- 苹果可弯曲屏幕新专利获准,折叠iPhone最快2020年现身?
- 正则表达式知识详解(转自晴天碧日)
- 网站后端_Python+Flask.0007.FLASK构造跳转之301跳转与302重定向?
- 设计模式2:工程模式(1)
- PS如何对JPG文件直接抠图
- .Net判断一个对象是否为数值类型探讨总结(高营养含量,含最终代码及跑分)...
- Lync Server 2010 安装部署系列三:添加DNS记录
- Windows 8实用窍门系列:9.Windows 8中使用FlipView
- 32位单精度浮点乘法器的FPGA实现