Thread API的详细介绍
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.toList;
public class Test {
public static void main(String[] args){
// ThradSleep.test();
// ThreadPriority.test();
// ThreadPriority.test();
// ThreadId.test();
// CurrentThread.test();
// ThreadInterrupt.test();
// ThreadIsInterrupted.test();
// ThreadIsInterrupted2.test();
// ThreadInterrupted.test();
// ThreadTest.test();
// ThreadJoin.test();
// FightQueryExample.test();
FlagThreadExit.test();`
}
}
/*
3.1.1 sleep方法介绍
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException
3.1.2 使用TimeUnit代替Thread.sleep
TimeUnit可以省去换算的步骤,比如线程想休眠3小时24分17秒88毫秒:
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(3);
TimeUnit.SECONDS.sleep(3);
TimeUnit.MILLISECONDS.sleep(3);
——我是真的需要把Java枚举部分看一下了。。。
*/
class ThradSleep{
public static void test(){
new Thread(()->{
long startTime=System.currentTimeMillis();
sleep(2_000L);
long endTime=System.currentTimeMillis();
System.out.println("Total: "+(endTime-startTime));
}).start();
long startTime=System.currentTimeMillis();
sleep(3_000L);
long endTime=System.currentTimeMillis();
System.out.println("Main Total: "+(endTime-startTime));
}
private static void sleep(long ms){
try{
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.2.1 yield方法介绍
调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态,这个方法一般不常用
——Java8的流和Lam,早看早练习,稳赚不赔!!!
3.2.2 yield和sleep
其本质区别:
1.sleep会导致当前线程暂停指定的之间,没有CPU时间片的消耗。(这个CPU时间片消耗指的是什么?)
2.yield只是对CPU调度器的一个暗示,可能导致线程上下文的切换。
3.sleep会使线程短暂block,会在指定的时间内释放CPU资源
4.yield会使RUNNING状态的Thread进入RUNABLE状态
5.sleep几乎会百分百地完成指定时间的休眠,而yield的提示并不一定有用
6.一个线程内调用了sleep,另一个线程调用了interrupt,这个调用了sleep的线程会捕获到
中断信号,而yield不会。
*/
class ThreadYield{
public static void test() {
IntStream.range(0,2).mapToObj(ThreadYield::create)
.forEach(Thread::start);
}
private static Thread create(int index) {
return new Thread(()->{
if (index == 0) {
Thread.yield();
}
System.out.println(index);
});
}
}
/*
3.3 设置线程优先级
public final void setPriority(int newPriority); :为线程设定优先级
public final int getPriority(); :获得线程的优先级
3.3.2 线程优先级源码分析
要点:
1.线程的优先级不能小于1,不能大于10;
2.如果优先级大于group的优先级,则优先级会被设置为group的优先级;
*/
class ThreadPriority{
public static void test(){
/* 实验设置优先级
Thread t1 = new Thread(()->{
while(true){
System.out.println("t1");
}
});
Thread t2 = new Thread(()->{
while(true){
System.out.println("t1");
}
});
t1.setPriority(3);
t2.setPriority(10);
t1.start();
t2.start();
*/
/* 实验线程优先级和组优先级关系
ThreadGroup group = new ThreadGroup("test");
group.setMaxPriority(7);
Thread t = new Thread(group,"test-thread");
t.setPriority(10);
System.out.println("t.getPriority():"+t.getPriority());
*/
/* 实验线程默认优先级 */
Thread t1 = new Thread();
System.out.println("t1 priority:"+t1.getPriority());
Thread t2 = new Thread(()->{
Thread t3 = new Thread();
System.out.println("t3 priority:"+t3.getPriority());
});
t2.setPriority(6);
t2.start();
System.out.println("t2 priority:"+t2.getPriority());
}
}
/*
3.4 获取线程ID
public long getId();
*/
class ThreadId{
public static void test(){
System.out.println("main id: "+Thread.currentThread().getId());
Thread t = new Thread("test");
System.out.println("t id: "+t.getId());
}
}
/*
3.5 获取当前线程
public static Thread currentThread();
*/
class CurrentThread{
public static void test(){
Thread t = new Thread() {
public void run() {
System.out.println(Thread.currentThread() == this);
}
};
t.start();
String name = Thread.currentThread().getName();
System.out.println("main".equals(name));
}
}
/*
3.6 设置线程上下文类加载器
public ClassLoader getContextClassLoader() :获取
public void setContextClassLoader(ClassLoader cl) :设置
*/
/*
*************************************************************************
*************************************************************************
*************************************************************************
*************************************************************************
*/
/*
3.7 线程interrupt
public void interrupt()
public static boolean interrupted()
public boolean isInterrupted()
——标记一下,我对interrupted()方法有点疑惑,这个方法是怎么办到把我一个
对象的标识位给复原了的。额,很尴尬,它调用了一个currentThread方法,
然后间接的设置了这个标志位,也就说明这个方法只能在某个Thread内部调用。
3.7.1 interrupt
如下方法的调用会使得当前线程进入阻塞状态:
1.Object的wait(),wait(long),wait(long,int)
2.Thread的sleep(long),sleep(long,int)
3.Thread的join,join(long),join(long,int)
4.InterruptibleChannel的io操作(这个很陌生!!!)
5.Selector的wakeup方法(这个很陌生!!!)
6.其他方法
如果另外一个线程调用阻塞线程的interrupt方法,则会打断这种阻塞,因此这种方法有时
会被称为可中断方法。打断一个线程并不等于该线程的生命周期的结束,仅仅是打断了当前
线程的阻塞状态。一旦线程在阻塞情况下被打断了,都会抛出一个称为InterruptedException
的异常,这个异常就像一个signal一样,高诉这个刚刚被中断的线程,哦,我刚刚是因为
中断被唤醒的啊~~~
要点:
interrupt()方法到底做了什么?在一个线程内部存在一个名为interrupt flag的标识
如果一个线程被interrupt,那么它的flag将会被设置。如果当前线程只在执行可中断的
方法,且被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除~~~(什么乱
七八糟啊啊啊啊)
*/
class ThreadInterrupt{
public static void test(){
Thread t = new Thread(()->{
try{
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
System.out.println("Oh,i am be interrupted...");
}
});
t.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
t.interrupt(); //去叫醒这个兄弟!!!
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.7.2 isInterrupted
isInterrupted是Thread的一个成员方法,它主要判断当前线程是否被中断,该方法仅仅是对
interrupt标识的一个判断,并不会影响标识发生任何改变。
——我感觉作者没有把阻塞和中断的许多细节给讲清楚
*/
class ThreadIsInterrupted{
public static void test(){
Thread t = new Thread(){
@Override
public void run() {
while(true){
//这个地方真的能被中断么???
//do nothing
}
}
};
t.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
System.out.printf("Thread is interrupted? %s",t.isInterrupted());
t.interrupt();
System.out.printf("Thread is interrupted? %s",t.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadIsInterrupted2{
public static void test(){
Thread t = new Thread(){
@Override
public void run() {
while(true){
try{
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
System.out.printf("I am be interrupted? %s\n",isInterrupted());
}
}
}
};
t.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
System.out.printf("Thread is interrupted? %s \n",t.isInterrupted());
t.interrupt();
System.out.printf("Thread is interrupted? %s \n",t.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
===========================================================================
实验总结
===========================================================================
实验结果分析:
1.调用interrupt这个方法了,这个方法只是简简单单的设置了一个flag
2.调用了isInterrupted这个方法了,也只是简简单单的去访问一下这个flag
3.线程中如果没有正在阻塞的方法, 你调用了interrupt是没有任何意义的,你设置的
这个flag根本没有方法去访问
4.如果线程正在某个方法上阻塞这,那么这个线程被分到时间片时,肯定先检查一下
这个flag的值,如果这个flag的值代表中断发生了,就让这个阻塞的方法返回
,同时将这个flag复原。
===========================================================================
*/
/*
3.7.3 interrupted
interrupted是一个静态方法,调用这个方法会擦除线程的interrupt表示,需要注意
的是,如果线程被打断了,那么第一次调用interrupted方法会返回true,并且立即擦
除interrupt标识~~~
——我想知道的是,这个东西有什么用!!!
*/
class ThreadInterrupted{
public static void test() {
Thread t = new Thread(){
@Override
public void run() {
while (true) {
/*
我有点不懂这个地方???,你调用的是一个
静态方法,来消除的是一个对象的标识。。。
*/
System.out.println(Thread.interrupted());
}
}
};
t.setDaemon(true);
t.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
t.interrupted();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.7.4 interrupt注意事项
如果一个线程设置了interrupt标识,那么接下来的可中断方法会立即中断~~~
——不然会造成中断信号丢失,问题更大
*/
class ThreadTest{
public static void test() {
System.out.println("Main thread is interrupted:"+Thread.interrupted());
Thread.currentThread().interrupt();
System.out.println("Main thread is interrupted now?"+Thread.currentThread().isInterrupted());
try{
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
System.out.println("I will be interrupted still.");
}
}
}
/*
3.8 线程join
public final void join() throws InterruptException
public final synchronized void join(long millis, int nanos) throws InterruptException
public final synchronized void join(long millis) throws InterruptException
3.8.1 线程join方法详解
有两种调用join的方法,main线程创建了A线程对象,然后调用A的join方法,这时候main线程会
等待A线程完成,然后在执行。还有一种就是在A线程内部的run方法中,调用了自生的join方法,不
知道在这种情况下会发生什么事情。
*/
class ThreadJoin{
public static void test() {
List<Thread> threads = IntStream.range(1,3)
.mapToObj(ThreadJoin::create)
.collect(toList());
threads.forEach(Thread::start);
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"#"+i);
shortSleep();
}
}
private static Thread create(int seq) {
return new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"#"+i);
}
},String.valueOf(seq));
}
private static void shortSleep() {
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.8.2 join方法结合实战
需求:
如果你有一个APP,主要用于查询航班信息,你的APP是没有这些实时数据的,当用户发起查询
请求时,你需要到各大航空公司的接口获取信息,最后同意整理加工返回到APP客户端。当然
JDK自带了很多高级工具,比如CountDownLatch和CyclieBarrier等都可以完成类似的功能,
该例子是典型的串行任务局部并行化处理~~~用户在APP客户端输入出发地北京和目的地上海后,
服务端接受到这个请求之后,先来验证用户的信息,然后到各大航空公司的接口查询信息,最后
经过整理加工返回给客户端,每一个航空公司的接口不会都一样,获取数据格式也不一样,查询
的速度也存在差异,如果再更航空公司进行串行化交互(逐个地查询),很明显客户端需要等待
很长的时间,这样的话,用户体验就会非常差。如果我们将每个航空公司的查询都交给一个线程
去工作,然后在它们结束之后同意对数据进行整理,这样就可以极大的节约时间,从而提高用户
体验效果。
*/
interface FightQuery{
List<String> get();
}
class FightQueryTask extends Thread implements FightQuery{
private final String origin;
private final String destination;
private final List<String> fightList = new ArrayList<>();
public FightQueryTask(String airline,String origin,String destination){
super("["+airline+"]");
this.origin=origin;
this.destination=destination;
}
@Override
public void run() {
System.out.printf("%s-query from %s to %s \n",getName(),origin,destination);
int randomVal = ThreadLocalRandom.current().nextInt(10);
try{
TimeUnit.SECONDS.sleep(randomVal);
this.fightList.add(getName()+"-"+randomVal);
System.out.printf("The Fight:%s list query successful\n",getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public List<String> get() {
return this.fightList;
}
}
class FightQueryExample{
private static List<String> fightCompany = Arrays.asList(
"CSA","CEA","HNA"
);
public static void test() {
List<String> results = search("SH","BJ");
System.out.println("=================result=================");
results.forEach(System.out::println);
}
private static List<String> search(String original,String dest){
final List<String> result = new ArrayList<>();
List<FightQueryTask> tasks = fightCompany.stream()
.map(f->createSearchTask(f,original,dest))
.collect(toList());
tasks.forEach(Thread::start);
tasks.forEach(t->{
try{
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
tasks.stream().map(FightQueryTask::get)
.forEach(result::addAll);
return result;
}
private static FightQueryTask createSearchTask(
String fight,
String original,
String dest
){
return new FightQueryTask(fight,original,dest);
}
}
/*
*************************************************************************
*************************************************************************
*************************************************************************
*************************************************************************
*/
/*
3.9 如何关闭一个线程
3.9.1 正常关闭
1.线程结束,生命周期正常结束
2.捕获中断信号关闭线程
用过new Thread的方法创建线程,因此在用这种方式创建的一个线程中往往会循环地执行
某个任务,比如心跳检查,不断地接受网络消息报文等,系统决定退出的时候,可以借助中断
线程的方式使其退出。
——因为用new Thread创建线程成本高,所以我们不能把它当快餐线程,所以我们
得让它疯狂的循环处理事务,又因为它在疯狂的循环,所以我们得用中断的方式
让它退出来。。。阿门
*/
class InterrupThreadExit{
public static void test() {
/*
没有调用可中断方法的,可以利用检测标志位。。。
*/
Thread t = new Thread(){
public void run(){
System.out.println("I will start work");
while(!isInterrupted()){
//working
}
System.out.println("I will be exiting...");
}
};
/*
调用了可中断方法的,可以通过捕获中断异常。。。
*/
Thread t2 = new Thread(){
public void run(){
System.out.println("I will start work");
while(true){
try{
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
break;
}
}
System.out.println("I will be exiting...");
}
};
t.start();
try {
TimeUnit.MINUTES.sleep(1);
System.out.println("System will be shutdown.");
t.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.使用volatile开关控制
由于interrupt标识很有可能白擦除,或者逻辑单元中不会调用任何可中断方法,所以使用
volatile修饰的开关flag关闭线程也是一种常用的做法:
*/
class FlagThreadExit{
static class MyTask extends Thread{
private volatile boolean close = false;
@Override
public void run() {
System.out.println("I will start work");
while(close&&!isInterrupted()){
//working
}
System.out.println("I will be exiting.");
}
public void close(){
this.close=true;
this.interrupt();
}
}
public static void test() {
MyTask t = new MyTask();
t.start();
try {
TimeUnit.MINUTES.sleep(1);
System.out.println("sys will be shutdown.");
t.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
3.9.2 异常退出
在一个线程的执行单元中,是不允许抛出checked异常的。如果线程在运行过程中需要捕获
checked异常并且判断是否还有运行下去的必要,那么此时可以将checked异常封装封装
成unchecked异常抛出,进而结束线程的生命周期。
3.9.3 进程假死
进程出现假死,可能是某个线程阻塞了,或者线程出现了死锁的情况。可以利用jstack、
jconsole、jvisualvm等工具进行诊断。
*/
——《Java高并发编程详解》
Thread API的详细介绍的更多相关文章
- 微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍
微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍 一.为什么会有 API Gateway 网关 随着微服务架构的流行,很多公司把原有的单 ...
- 微服务架构学习与思考(11):开源 API 网关02-以 Java 为基础的 API 网关详细介绍
微服务架构学习与思考(11):开源 API 网关02-以 Java 为基础的 API 网关详细介绍 上一篇关于网关的文章: 微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Ngi ...
- 【并发编程】关于Thread类的详细介绍
多线程编程基础--Thread类 Thread类是Java中实现多线程编程的基础类.本篇博客就来介绍下Thread类的常用API和常见用法. Thread类常用的方法如下: Thread.active ...
- Xcode中c++&Object-C混编,详细介绍如何在cocos2dx中访问object函数以及Apple Api
转自:http://www.himigame.com/iphone-cocos2dx/743.html Cocos2dx系列博文的上一篇详细介绍了如何在Xcode中利用jni调用Android的Jav ...
- Java 并发专题 : Executor详细介绍 打造基于Executor的Web服务器
转载标明出处:http://blog.csdn.net/lmj623565791/article/details/26938985 继续并发,貌似并发的文章很少有人看啊~哈~ 今天准备详细介绍java ...
- 城市经纬度 json 理解SignalR Main(string[] args)之args传递的几种方式 串口编程之端口 多线程详细介绍 递归一个List<T>,可自己根据需要改造为通用型。 Sql 优化解决方案
城市经纬度 json https://www.cnblogs.com/innershare/p/10723968.html 理解SignalR ASP .NET SignalR 是一个ASP .NET ...
- kvm详细介绍
KVM详解,太详细太深入了,经典 2016-07-18 19:56:38 分类: 虚拟化 原文地址:KVM详解,太详细太深入了,经典 作者:zzjlzx KVM 介绍(1):简介及安装 http:// ...
- 详细介绍如何自研一款"博客搬家"功能
前言 现在的技术博客(社区)越来越多,比如:imooc.spring4All.csdn.cnblogs或者iteye等,有很多朋友可能在这些网站上都发表过博文,当有一天我们想自己搞一个博客网站时就会发 ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
随机推荐
- Win8Metro(C#)数字图像处理--2.34直方图规定化
原文:Win8Metro(C#)数字图像处理--2.34直方图规定化 [函数名称] WriteableBitmap HistogramSpecificateProcess(WriteableBi ...
- 什么是DirectShow?
DirectShow是微软出的用于流媒体开发的开发包.开发语言是C++,没提供C#接口的调用方式.
- Android零基础入门第27节:正确使用padding和margin
原文:Android零基础入门第27节:正确使用padding和margin 前面两期我们学习了LinearLayout线性布局的方向.填充模型.权重和对齐,那么本期我们来学习LinearLayout ...
- Advanced Installer 11.9基于IIS打包札记(For MySQL)
原文:Advanced Installer 11.9基于IIS打包札记(For MySQL) Mysql免安装前期部署 下载绿色命令行版本的mysql,将其放入到发布的程序发布包内,执行Update批 ...
- Decision Tree
Decision Tree builds classification or regression models in the form of a tree structure. It break d ...
- 从IntToHex()说起,栈/堆地址标准写法 good
学习中的一些牢骚.栈/堆地址标准写法. 2017-02-12 • 杂谈 • 暂无评论 • 老衲 •浏览 226 次 我一直都在寻找各种业务功能的最简单写法,用减法的模式来开发软件.下面是我的写法,如果 ...
- CDC-更改数据捕获存储过程 (Transact-SQL)-学习
背景: 在SQLServer2008之前,对数据变更的捕获通常使用触发器.时间戳等低效高成本的功能来实现,所以很多系统都没有做数据变更或者仅仅对核心表做监控. 适用环境: 仅在SQLServer200 ...
- Win7 访问 数据库 慢
不让TCP/IP调谐拖累网速 在Windows Server 2008工作环境中,下载访问网络中大容量的文件内容时,我们有时会感觉到网络连接速度非常缓慢,严重的时候还会出现不能访问的现象.遭遇这类故障 ...
- mybatis链接数据库
DBTools类 public class DBTools { // 加载mybatis文件 public static SqlSession getSession() { //加载配置文件 Inpu ...
- C# 设计模式,简单工厂
C# 实现计算机功能 (封装,继承,多态) using System; using System.Collections.Generic; using System.Linq; using Syste ...