创建线程的三种方法,线程的生命周期,sleep,yield,join,wait 和notify,线程组,守护线程,线程的优先级

如何创建线程

Java 中创建线程的方法有三种:

  1. 继承 Thread 类创建线程

新建一个类继承 Thread 类,并重写 Thread 类的 run() 方法。
创建 Thread 子类的实例。
调用该子类实例的 start() 方法启动该线程。

代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloThread1 {
static class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("Hello Thread");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
}
}

2.实现 Runnable 接口创建线程:

创建一个类实现 Runnable 接口,并重写该接口的 run() 方法。
创建该实现类的实例。
将该实例传入 Thread(Runnable r) 构造方法中创建 Thread 实例。
调用该 Thread 线程对象的 start() 方法。

代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloThread1 {
static class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("Hello Thread");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
}
}
  1. 使用 Callable 和 FutureTask 创建线程:

创建一个类实现 Callable 接口,并重写 call() 方法。
创建该 Callable 接口实现类的实例。
将 Callable 的实现类实例传入 FutureTask(Callable callable) 构造方法中创建 FutureTask 实例。
将 FutureTask 实例传入 Thread(Runnable r) 构造方法中创建 Thread 实例。
调用该 Thread 线程对象的 start() 方法。
调用 FutureTask 实例对象的 get() 方法获取返回值。

代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class HelloThread3 {
static class ThreadDemo implements Callable<String> {
@Override
public String call() {
System.out.println("Hello Thread");
return "Callable return value";
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
FutureTask<String> futureTask = new FutureTask<String>(threadDemo);
Thread thread = new Thread(futureTask);
thread.start();
try {
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}


线程的生命周期

关于Java中线程的生命周期,首先看一下下面这张图:

上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基本上掌握了。


线程的基本状态

新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):当调用方法t.start()时,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;同样还有几种情况会进行就绪状态,请参见上图。

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:请参见上图。

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


线程的基本操作

sleep:使当前线程休眠指定毫秒

yield:使当前线程让出CPU,从运行状态转到可运行状态。注意仅仅是让出,让出之后也会加入到抢占资源的队伍中。

join:把指定的线程加入到当前线程,比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B

线程停止:
Thread本身提供了一个stop方法,但是这个不推荐使用。因为使用stop的时候会暴力终止线程,从而造成数据不一致。
优雅的停止线程的代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class StopThread {
static class ThreadDemo extends Thread {
volatile boolean stopMe = false;
public void stopMe(){
this.stopMe=true;
}
@Override
public void run() {
while (true) {
if (stopMe) {
System.out.println("程序结束");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.stopMe();
}
}

以一个volatile修饰的变量stopMe来控制线程的停止。

线程中断:

线程中断的相关方法分别是这三个

1
2
3
public void interrupt() ; //中断线程
public boolean isInterrupted(); //判断线程是否被中断
public static boolean interrupted(); //判断线程是否被中断,并清除当前中断状态

线程中断的代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class InterruptThread {
static class ThreadDemo extends Thread {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("程序结束");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.interrupt();
}
}

wait 和notify
这两个方法是JDK为了支持多线程之间的协作而提供的。
当在线程A中调用了obj.wait()方法时,线程A会停止执行进入等待状态。直到其他线程调用obj.notify()时才会进入阻塞状态继而等待获取锁。
请看下方示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class WaitNotifyThread {
public static Object obj = new Object(); static class WaitThreadDemo extends Thread {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("WaitThread wait,time=" + System.currentTimeMillis());
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("WaitThread end,time=" + System.currentTimeMillis());
}
}
} static class NotifyThreadDemo extends Thread {
@Override
public void run() {
synchronized (obj) {
System.out.println("NotifyThread notify,time=" + System.currentTimeMillis());
obj.notify();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("NotifyThread end,time=" + System.currentTimeMillis());
}
}
} public static void main(String[] args) {
WaitThreadDemo waitThreadDemo = new WaitThreadDemo();
NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo();
waitThreadDemo.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyThreadDemo.start();
}
}

在上方的代码中,Wait线程会首先获取到obj的锁,当它执行wait方法时就会释放obj的锁并进入等待状态。这个时候Notify线程可以获取到obj的锁,并且唤醒Wait线程,但是因为此时Notify线程是睡眠了2秒钟之后才释放的obj的锁,所以Wait线程获取锁的时候Notify线程已经执行完毕了。
此程序的运行结果:

1
2
3
4
WaitThread wait,time=1553088237753
NotifyThread notify,time=1553088237862
NotifyThread end,time=1553088239867
WaitThread end,time=1553088239867

suspen和resume
它们两个的功能是挂起线程和继续执行,被suspen挂起的线程必须被resume唤醒才可以继续执行。乍看起来 可以实现wait和notify的功能,不过我可不推荐你使用它们,和wait之后会释放锁不同,suspen挂起之后依然会持有锁,这个可就非常危险了。

线程组
在一个系统中如果线程数量众多而又功能比较一致,就可以把这些线程放到一个线程组里。
线程组示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ThreadGroupDemo {
static class ThreadDemo extends Thread {
@Override
public void run() {
while (true){
System.out.println("I am "+Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName());
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadGroup threadGroup=new ThreadGroup("groupDemo");
Thread t1=new Thread(threadGroup,new ThreadDemo(),"t1");
Thread t2=new Thread(threadGroup,new ThreadDemo(),"t2");
t1.start();
t2.start();
}
}

守护线程
在线程的世界里,由我们自己创建的线程叫用户线程。而一些系统创建的线程,如垃圾回收线程等被称之为守护线程,如果想要把一个用户线程设置为守护线程可以在线程的调用线程的start方法前设置线程的daemon属性为true;

1
t1.setDaemon(true);

当一个程序中只有守护线程的时候这个程序也就要结束了。

线程的优先级

Java中的线程可以有自己的优先级,优先级高的线程在进行资源抢占的时候往往会更有优势。线程饥饿现象就是由于线程的优先级低无法抢占资源而引起的。
在Java中线程的优先级可以设置的范围是1到10之间,数字越大优先级越高。Java线程创建默认的优先级是5,我们可以线程start之前通过如下方式设置线程的优先级。

1
t1.setPriority(1);

本文所有源码https://github.com/shiyujun


Hello,Thread的更多相关文章

  1. java 创建线程的三种方法Callable,Runnable,Thread比较及用法

    转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...

  2. NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL

    .NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL,异常处理,线程取消 今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主 ...

  3. future封装了callable,thread封装future。

    三.使用Callable,Future返回结果 总结:future封装了callable,thread封装future.将callable的返回结果封装在future中,thread封装future, ...

  4. 【并行计算-CUDA开发】CUDA编程——GPU架构,由sp,sm,thread,block,grid,warp说起

    掌握部分硬件知识,有助于程序员编写更好的CUDA程序,提升CUDA程序性能,本文目的是理清sp,sm,thread,block,grid,warp之间的关系.由于作者能力有限,难免有疏漏,恳请读者批评 ...

  5. Runnable,Thread实现多线程以及Runnable的同步资源共享

    (一) 实现多线程有两种方式 (1) 继承Thread类,重写run()方法,如以下例子 class MyThread extends Thread{ public void run(){ // } ...

  6. Handler,Thread,Looper之间关系小结

    http://blog.csdn.net/sunxingzhesunjinbiao/article/details/6794840 (1) Looper类别用来为一个线程开启一个消息循环.默认情况下A ...

  7. iOS 中NSOperationQueue,Grand Central Dispatch , Thread的上下关系和区别

    In OS X v10.6 and later, operation queues use the libdispatch library (also known as Grand Central D ...

  8. ClassLoader,Thread.currentThread().setContextClassLoader,tomcat的ClassLoader

    实际上,在Java应用中所有程序都运行在线程里,如果在程序中没有手工设置过ClassLoader,对于一般的java类如下两种方法获得的ClassLoader通常都是同一个 this.getClass ...

  9. Java多线程Thread.yield(),thread.join(), Thread.sleep(200),Object.wait(),Object.notify(),Object.notifyAll()的区别

    Thread.yield(),在某个线程里调用Thread.yield(),会使这个线程由正在运行的running状态转变为等待cpu时间片的runable状态.join()是Thread类的一个非s ...

  10. .NET 异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

随机推荐

  1. 【算法】BILSTM+CRF中的条件随机场

    BILSTM+CRF中的条件随机场 tensorflow中crf关键的两个函数是训练函数tf.contrib.crf.crf_log_likelihood和解码函数tf.contrib.crf.vit ...

  2. python单元测试框架unittest总结

    unittest.TestCase:TestCase类,所有测试用例类继承的基本类. class BaiduTest(unittest.TestCase): TestCase类的属性如下: setUp ...

  3. poj1988 Cube Stacking 带权并查集

    题目链接:http://poj.org/problem?id=1988 题意:有n个方块,编号为1-n,现在存在两种操作: M  i  j  将编号为i的方块所在的那一堆方块移到编号为j的方块所在的那 ...

  4. mysql_config not found和error: command 'gcc' failed with exit status 1

    要想使python可以操作mysql 就需要MySQL-python驱动,它是python 操作mysql必不可少的模块. 下载地址:https://pypi.python.org/pypi/MySQ ...

  5. 编译php-5.3.28

    1. 下载php-5.3.28 2. 编译/安装 ./configure --prefix=/usr/local/php --enable-fpm --enable-maintainer-zts -- ...

  6. 2018-2019-2 网络对抗技术 20165206 Exp6 信息搜集与漏洞扫描

    - 2018-2019-2 网络对抗技术 20165206 Exp6 信息搜集与漏洞扫描 - 实验任务 (1)各种搜索技巧的应用 (2)DNS IP注册信息的查询 (3)基本的扫描技术:主机发现.端口 ...

  7. percona-toolkit安装

    https://www.percona.com/downloads/percona-toolkit/LATEST/ #下载wget https://www.percona.com/downloads/ ...

  8. SpringBoot 添加mybatis generator 自动生成代码插件

    自动生成数据层代码,提高开发效率 1.pom添加插件,并指定配置文件路径 <!-- mybatis generator 自动生成代码插件 --> <plugin> <gr ...

  9. 在SOUI中使用布局模板

    概要 注意:布局模板是SOUI 2.8.0.4 新增加的功能.之前版本不支持. SOUI的listview等一系统控件支持通过模板来创建列表项,这里要说的模板不是指listview中的使用的列表项模板 ...

  10. 第三次作业-结对编程(wordcount)

    GIT地址 https://github.com/gentlemanzq/WordCount.git GIT用户名  gentlemanzq 结对伙伴博客地址 https://home.cnblogs ...