多线程意义

在同一时间需要完成多项任务时需要多线程。

参考视频:看动画,学Java多线程教程

创建线程三种方式

继承Thread类

1
2
3
4
5
6
7
8
package main;
//自定义Thread类
public class MyThread extends Thread {
@Override
public void run(){
System.out.println("将执行任务写到run方法里");
}
}
1
2
3
4
5
6
7
package main;
public class Main{
//创建MyThread实例
MyThread mythread = new MyThread();
//启动线程
myThread.start();
}

实现Runnable接口

Runnable没有启动线程的能力,需要搭配thread来用。

1
2
3
4
5
6
7
8
package main;
//自定义任务
public class Task implements Runnable {
@Override
public void run(){
System.out.println("将执行任务写到run方法里");
}
}
1
2
3
4
5
6
7
8
9
10
11
package main;
public class Main {
public static void main(String[] args) {
//创建Task实例
Task task = new Task();
//创建线程
Thread thread = new Thread(task);
//启动线程
thread.start();
}
}

实现Callable接口

Callable没有启动线程的能力,需要搭配thread来用。同时,Callable需要返回泛型。

1
2
3
4
5
6
7
8
9
package main;
import java.util.concurrent.Callable;
//自定义任务
public class Result implements Callable<String> {
@Override
public String call() throws Exception {
return "将要执行的任务写在call方法里面并返回执行的结果";
}
}
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
package main;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Main {
public static void main(String[] args) throws InterruptedException{
//创建Result示例
Result result = new Result();
//创建FutureTask实例
FutureTask<String> futureTask = new FutureTask<>(result);
//创建线程
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
try {
//获取执行结果
String str = futureTask.get();
//输出执行结果
System.out.println(str);
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
}

总结

创建方式 使用场景
Thread 单继承(不建议在开发用)
Runnable 无返回任务
Callable 有返回任务

获取当前执行任务的线程 currentThread()

1
2
3
4
5
6
7
8
9
package main;
public class Main {
public static void main(String[] args){
//获取当前正在执行任务的线程
Thread thread = Thread.currentThread();
//输出当前线程
System.out.println(thread);
}
}

输出结果:

1
Thread[main,5,main]

main:线程的名称

5:线程的优先级

main:线程所属的线程组名称

获取和设置线程的名称、优先级

获取线程名称 getName()

1
String str = thread.getName();

设置线程名称 setName(String name)

1
thread.setName("在这里设置名称");

获取线程优先级 getPriority()

1
int priority = thread.getPriority();

设置线程优先级 setPriority()

1
thread.setPriority();

常用优先级常量

1
2
3
4
5
6
//最小优先级
public final static int MIN_PRIORITY = 1;
//最大优先级
public final static int MAX_PRIORITY = 10;
//默认优先级
public final static int NORM_PRIORITY = 5;
1
2
//调用优先级常量(最大)
thread.setPriority(Thread.MAX_PRIORITY);

run()与start()区别

位置

虽然都在Thread,但是run方法重写自Runnable接口。

类型

run是非同步方法,start是同步方法。

作用

run存放执行代码,start启动线程,启动后执行run方法。

是否产生线程

run不产生线程,start只产生一个线程。

1
2
3
4
5
6
7
8
9
10
11
12
package main;
public class Task implements Runnable {
@Override
public void run(){
//获取当前正在执行任务的线程
Thread thread = Thread.currentThread();
//获取线程名称
String threadName = thread.getName();
//打印线程的名称
System.out.println(threadName);
}
}
1
2
3
4
5
6
7
8
9
10
package main;
public class Main{
public static void main(String[] args){
//创建任务
Task task = new Task();
Thread thread = new Thread(task);
//调用run方法
thread.run();
}
}

输出结果:

1
main

调用次数

run可调用无限次,start只能调用一次。

进入休眠 sleep()

1
2
3
4
5
/**
*@throws IllegalArgumentException 如果millis值为负数
*@throws InterruptedException 如果有任何线程中断了当前线程。引发此异常时,将清除当前的中断状态。
*/
public static native void sleep(long millis) throws InterruptedException
1
2
3
4
5
6
/**
*@param nanos 额外指定睡眠时间的纳秒部分。范围为0-999999
*@throws IllegalArgumentException 如果millis值为负数,或者nanos值不在范围内
*@throws InterruptedException 如果有任何线程中断了当前线程。引发此异常时,将清除当前的中断状态。
*/
public static void sleep(long millis, int nanos) throw InterruptedException

中断线程

停止正在运行的线程

中断标记 interrupt()

1
2
3
4
5
6
7
8
9
10
package main;
public class Task implements Runnable {
@Override
public void run(){
//无限打印语句
while(true){
System.out.println("正在运行");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package main;
public class Main{
public static void main(String[] args) throws InterruptedException{
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
//使线程休眠1秒钟
Thread.sleep(1000);
//中断程序
thread.interrupt();
}
}

输出结果:

1
2
3
4
正在运行
正在运行
正在运行
...(不会停止)

原因:interrupt只是标记,中断操作要在run()方法里面操作。

中断操作 isInterrupt()和interrupted()

方法名称 类型 作用
isInterrupted 非静态方法 判断线程是否被中断
interrupted 静态方法(可通过类直接调用) 判断线程是否被中断,并清除中断标记

isInterrupted():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main;
public class Task implements Runnable {
@Override
public void run(){
//无限打印语句
while(true){
//获取当前正在执行的线程
Thread thread = Thread.currentThread();
//当线程被中断时
if(thread.isInterrupted()){
//结束循环
break;
}
System.out.println("正在运行");
}
}
}

interrupted():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main;
public class Task implements Runnable {
@Override
public void run(){
//无限打印语句
while(true){
//当线程被中断时
if(Thread.interrupted()){
//结束循环
break;
}
System.out.println("正在运行");
}
}
}

一样效果,可结束线程

isInterrupted():

1
2
3
4
5
6
7
8
9
...
@Override
public void run(){
while(true){
Thread thread = Thread.currentThread();
//打印线程是否被中断
System.out.println(thread.isInterrupted());
}
}

输出结果:

1
2
3
4
5
6
7
false
false
...
false
true
true
...

interrupted():

1
2
3
4
5
6
7
8
...
@Override
public void run(){
while(true){
//打印线程是否被中断
System.out.println(Thread.interrupted());
}
}

输出结果:

1
2
3
4
5
6
7
8
false
false
...
false
true
false
false
...

停止休眠中的线程

1
2
3
4
5
6
7
8
9
10
...
@Override
public void run(){
try{
//使当前线程休眠10秒钟
Thread.sleep(10000);
}catch (InterruptedException e){
e.printStackTrace();
}
}

输出结果:

1
2
3
4
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at com.example.demo.Task.run(Task.java:8)
at java.base/java.lang.Thread.run(Thread.java:833)

让别的线程得到更多执行权 yield()

在run()方法中使用Thread.yield()方法可以让别的线程得到更多执行权,但不是放弃执行权。

1
2
3
4
5
6
7
8
9
public class PrintTask implements Runnable{
@Override
public void run(){
while (ValueTask.value==0){
Thread.yield();//让另一个线程得到更多执行权,减少空转
}
System.out.println(ValueTask.value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ValueTask implements Runnable{
public static int value = 0;
@Override
public void run(){
System.out.println(6);
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
value = 100;
}
}

等待线程死亡 join()

在一个线程中的run()方法中调用另一个线程的join()方法时,调用线程将被阻塞,直到另一个线程执行完为止(一定要在join()之前先start(),不然直接join直接结束)

1
2
3
public final void join() throws InterruptedException {
join(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Task implements Runnable{
@Override
public void run(){
JoinTask joinTask = new JoinTask();
Thread joinThread = new Thread(joinTask);
System.out.println(JoinTask.value);
try {
joinThread.start();//一定要在join()之前先start()
joinThread.join();
System.out.println(JoinTask.value);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
public class JoinTask implements Runnable{
public static int value = 0;
@Override
public void run(){
value = 100;
}
}

守护线程(后台线程) daemon()

设置守护线程 setDaemon()

1
public final void setDaemon(boolean on)
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args){
PrintTask printTask = new PrintTask();
Thread printThread = new Thread(printTask);
printThread.setDaemon(true);//要在start之前设置守护线程
printThread.start();
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
public class PrintTask implements Runnable{
@Override
public void run(){
while (true){
System.out.println("线程运行中");
}
}
}

线程在主线程结束时结束。

判断线程是否为后台线程 isDaemon()

1
public final boolean isDaemon()

线程是否死亡 isAlive()

1
public final native boolean isAlive();

*同步锁 synchronized

线程获得同步锁对象后,只能该线程占用这个对象,其他线程必须等待该线程释放了该对象才能占用这个对象。解决线程安全问题。

1
2
3
synchronized(object){}
访问修饰符 synchronized 返回值类型 方法名(参数类型 参数名称){}
访问修饰符 static synchronized 返回值类型 方法名(参数类型 参数名称){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args){
TicketingTask ticketingTask = new TicketingTask();//创建一个任务,多个线程执行该任务
Thread ticketingThread1 = new Thread(ticketingTask);
Thread ticketingThread2 = new Thread(ticketingTask);
Thread ticketingThread3 = new Thread(ticketingTask);
ticketingThread1.setName("1号");
ticketingThread2.setName("2号");
ticketingThread3.setName("3号");
ticketingThread1.start();
ticketingThread2.start();
ticketingThread3.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TicketingTask implements Runnable{
private int ticket = 10;
@Override
public void run(){
while (ticket>0){
synchronized (this){
if(ticket>0){//要双重判断,因为在卖最后一票的时候另外2个线程同时锁外面等待。
System.out.println(ticket+"号票卖出,窗口为"+Thread.currentThread().getName());
ticket--;
}
}
}
}
}

可同步的内容

同步代码块

1
2
3
synchronized(对象){
//需要被同步的代码
}

同步方法

1
2
public synchronized void show (String name){
}

同步类

1
synchronized(类名.class){}

死锁

死锁指2个或2个以上的线程争夺彼此的锁,造成阻塞,程序永远处于阻塞状态。

死锁产生的条件

两个或两个以上的线程

两个或两个以上的锁

两个或两个以上的线程持有不同锁

持有不同锁的线程争夺对方的锁

示例

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args){
LockA lockA = new LockA();
LockB lockB = new LockB();
lockA.start();
lockB.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LockA extends Thread{
@Override
public void run(){
printA();
//调用不同的锁
}
public static synchronized void printA(){
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("A");
LockB.printB();
//争夺B线程的锁
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LockB extends Thread {
@Override
public void run(){
printB();
//调用不同的锁
}
public static synchronized void printB(){
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("B");
LockA.printA();
//争夺A线程的锁
}
}

等待唤醒机制 wait(), notify()

wait()

1
2
3
4
5
6
/**
* 使当前线程等待,直到被唤醒。
* @throws IllegalMonitorStateException 如果当前线程没有锁,则引发该异常。
* @throws InterruptedException 如果当前线程被中断,则引发该异常。
**/
public final void wait() throws InterruptedException

notify()

1
2
3
4
5
/**
*唤醒单个线程。
*@throws IllegalMonitorStateException 如果当前线程没有拥有锁,则引发该异常。
*/
public final native void notify()

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LockA extends Thread{
@Override
public void run(){
printA();
}
public static synchronized void printA(){
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("A");
synchronized (LockB.class){//使当前线程有对应的锁
LockB.class.notify();//唤醒线程
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LockB extends Thread {
@Override
public void run(){
printB();
}
public static synchronized void printB(){
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
try{
LockB.class.wait();//等待唤醒
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("B");
}
}

唤醒所有线程 notifyAll() ,使用方法和notify类似

wait()和sleep()区别

sleep() wait()
位置 Thread类 Object类
是否需要当前线程拥有锁? 不需要 需要
是否支撑手动唤醒? 不支持 notify()、notifyAll()
是否支持自动唤醒? sleep(long millis) wait(long timeout)
是否支持中断? interrupt interrupt
是否释放锁?
线程状态 TIMED_WAITING WAITING、TIMED_WAITING