首页>>后端>>java->线程的4种创建方法和使用详解!

线程的4种创建方法和使用详解!

时间:2023-11-29 本站 点击:7

一、概述

在开始学习Thread之前,我们先来了解一下线程和进程之间的关系:

线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。

由上描述,可以得知线程作为cpu的基本调度单位,只有把多线程用好,才能充分利用cpu的多核资源。

本文基于JDK 8(也可以叫JDK 1.8)。

二、线程使用

2.1 启动线程

创建线程有四种方式:

实现Runnable接口

继承Thread类

使用JDK 8 的Lambda

使用Callable和Future

2.1.1 Runnable创建方式

publicclassMyThreadimplementsRunnable{@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName());}}
Threadthread=newThread(newMyThread());thread.start();

2.1.2 继承Thread创建方式

publicclassMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName());}}
MyThreadthread=newMyThread();thread.start();

以上代码有更简单的写法,如下:

Threadthread=newThread(){@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName());}};thread.start();

2.1.3 Lambda创建方式

newThread(()->System.out.println(Thread.currentThread().getName())).start();

2.1.4 使用Callable和Future

看源码可以知道Thread的父类是Runnable是JDK1.0提供的,而Callable和Runnable类似,是JDK1.5提供的,弥补了调用线程没有返回值的情况,可以看做是Runnable的一个补充,下面看看Callable的实现。

publicclassMyThreadimplementsCallable<String>{@OverridepublicStringcall()throwsException{System.out.println(Thread.currentThread().getName());returnThread.currentThread().getName();}}
Callable<String>callable=newMyThread();FutureTask<String>ft=newFutureTask<>(callable);newThread(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-10join(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释放锁的代码如下:

publicclassSynchronizedTestextendsThread{intnumber=10;publicsynchronizedvoidfirst(){System.out.println("thisisfirst!");number=number+1;}publicsynchronizedvoidsecord()throwsInterruptedException{System.out.println("thisissecord!!");Thread.sleep(1000);//this.wait(1000);number=number*100;}@Overridepublicvoidrun(){first();}}
SynchronizedTestsynchronizedTest=newSynchronizedTest();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 尚未启动状态

Threadthread=newThread(newMyThread());thread.start();0

3.2.2 RUNNABLE 运行状态

Threadthread=newThread(newMyThread());thread.start();1

3.2.3 BLOCKED 阻塞状态

使用synchronized同步阻塞实现,代码如下:

Threadthread=newThread(newMyThread());thread.start();2
Threadthread=newThread(newMyThread());thread.start();3

3.2.4 WAITING 永久等待状态

Threadthread=newThread(newMyThread());thread.start();4
Threadthread=newThread(newMyThread());thread.start();5

唤醒线程: 可使用 notify/notifyAll 方法,代码如下:

Threadthread=newThread(newMyThread());thread.start();6

使线程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设置上时间即可,代码如下:

Threadthread=newThread(newMyThread());thread.start();7

调用代码还是一样的,如下:

Threadthread=newThread(newMyThread());thread.start();//主线程挂起200毫秒,等thread执行完成Thread.sleep(200);//输出TIMED_WAITINGSystem.out.println(thread.getState());Threadthread=newThread(newMyThread());thread.start();6

3.2.6 TERMINATED 完成状态

Threadthread=newThread(newMyThread());thread.start();9

四、死锁

根据前面的知识,我们知道使用sleep的时候是不释放锁的,所以利用这个特性我们可以很轻易的写出死锁的代码,具体的流程如图(图片来源于杨晓峰老师文章):

代码如下:

publicclassMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName());}}0

运行上面的代码,程序会处于无限等待之中。

五、总结

根据上面的内容,我们已经系统的学习Thread的使用了,然而学而不思则罔,最后留一个思考题:根据本文介绍的知识,怎么能避免死锁?(哈哈,卖个关子,根据文章的知识点组合可以得出答案)

源码下载:github.com/vipstone/java-core-example.git

参考文档

docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html

作者:Java中文社群


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/87.html