Java多线程笔记

本文最后更新于:2025年2月18日 下午

Java多线程

多线程实现方式

一:继承Thread
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
package Example1;

public class Demo {

public static void main(String[] args) {
//创建线程对象
Test test = new Test();
Test test1 = new Test();
//设置线程名称
test.setName("窗口1:");
test1.setName("窗口2:");
//开启线程
test.start();
test1.start();
}
}

class Test extends Thread {

//共享资源
public static int ticket = 10;

@Override
public void run() {
while (true){
//同步代码块
synchronized (SellPort1.class){
if (ticket == 0){
break;
}else {
//票数减一
ticket--;
//打印当前线程名称和剩余票数
System.out.println(Thread.currentThread().getName()+"卖出一张票!剩余票数"+ticket);
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}

二:实现Runnable接口
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
47
package Example1;

public class Demo {

public static void main(String[] args) {
//创建线程对象
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
//设置线程名称
t1.setName("窗口1:");
t2.setName("窗口2:");
//开启线程
t1.start();
t2.start();

}
}

class Test implements Runnable {

//共享资源
public static int ticket = 10;

@Override
public void run() {
while (true){
//同步代码块
synchronized (SellPort1.class){
if (ticket == 0){
break;
}else {
//票数减一
ticket--;
//打印当前线程名称和剩余票数
System.out.println(Thread.currentThread().getName()+"卖出一张票!剩余票数"+ticket);
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
三:实现Callable接口
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package Example1;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Callable;

public class Demo {

public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Test对象
Test t = new Test();
//创建FutureTask对象,它可以管理多线程运行的结果值
FutureTask<Integer> futureTask = new FutureTask<>(t,);
FutureTask<Integer> futureTask2 = new FutureTask<>(t);
//创建线程对象
Thread t1 = new Thread(futureTask,"线程1");
Thread t2 = new Thread(futureTask2,"线程2");
//启动线程
t1.start();
t2.start();

//获取线程结果值
Integer o = futureTask.get();
Integer o2 = futureTask.get();
//打印输出
System.out.println(o);
System.out.println(o2);

}
}

class Test implements Callable<Integer> {

//共享资源
public static int ticket = 10;

@Override
public Integer run() {
while (true){
//同步代码块
synchronized (Test.class){
if (ticket == 0){
break;
}else {
//票数减一
ticket--;
//打印当前线程名称和剩余票数
System.out.println(Thread.currentThread().getName()+"卖出一张票!剩余票数"+ticket);
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//如果是线程1,返回1,否则返回2
if(Thread.currentThread().getName() == "线程1"){
return 1;
}else{
return 2;
}
}
}

成员方法

方法名称 说明
public final String getName() 返回此线程的名称
public final synchronized void setName(String name) 设置线程名称
public static native Thread currentThread(); 获取当前线程对象
public static native void sleep(long millis) 让线程休眠指定时间
public final void setPriority(int newPriority) 设置线程优先级(默认5,数值越大越优先)
public final int getPriority() 获取线程优先级(默认5,数值越大越优先)
public final void setDaemon(boolean on) 设置线程守护
public static native void yield(); 出让线程
public final void join() 插入线程

线程生命周期

创建 -> 就绪 -> 运行 -> (阻塞 -> 等待) -> 结束

同步关键字 synchronized

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
synchronized 可修饰方法 ->  同步方法
public final synchronized void setName(String name){}

//修饰代码块,方法内为锁对象,锁对象可以任意创建,但必须唯一
synchronized (Test.class){
if (ticket == 0){
break;
}else {
//票数减一
ticket--;
//打印当前线程名称和剩余票数
System.out.println(Thread.currentThread().getName()+"卖出一张票!剩余票数"+ticket);
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

//小技巧,同步方法步骤
1、循环
2、同步代码块
3、判断共享数据是否到末尾,如果到了末尾就break或者怎么怎么样
4、判断共享数据是否到末尾,如果没到末尾怎么怎么样

Lock锁,手动加解锁

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
package ThreadLock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread {

static int counter = 0;
static Lock lock = new ReentrantLock();

@Override
public void run() {
while (true){
lock.lock();

if (counter == 100){
/*
此处跳出,会带着锁跳出while循环,这样其他线程就一直在等待,导致程序无法停止。
解决方法一:在跳出前加一行解锁代码或者在while循环外加一行解锁代码
解决方法二【推荐】:用try-catch-finally处理要锁定的代码,把释放锁放在finally代码段中
*/
break;
}else {
counter++;
System.out.println(Thread.currentThread().getName()+"正在售卖第"+counter+"张票!!!");
}
lock.unlock();
}
lock.unlock();
}
}

需要避免的情况 --> 【死锁】

死锁通常是因为嵌套锁产生的,尽量避免嵌套。

等待唤醒【生产者和消费者】

方法名称 说明
public final void wait() 线程等待,直到被其他线程唤醒
public final native void notify(); 随机唤醒单个线程
public final native void notifyAll(); 唤醒(同一 JVM 中或同一个对象上)等待的所有线程

启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package wait;

public class MyThread{

public static void main(String[] args) {
Cook cook = new Cook();
Eat eat = new Eat();
cook.setName("厨师");
eat.setName("顾客");
cook.start();
eat.start();
}
}

桌面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package wait;

public class Desk {

//桌上是否有面
public static int foodStatus = 0;

//顾客总共要吃的碗数
public static int count = 10;

//锁对象
public static Object lock = new Object();
}

厨师类(生产者)

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
package wait;

public class Cook extends Thread {

@Override
public void run() {
while (true){
synchronized (Desk.lock) {
if (Desk.count == 0){
break;
}else {
if (Desk.foodStatus == 1){
try {
Desk.lock.wait();
}catch (Exception e){
e.printStackTrace();
}
}else {
System.out.println("厨师做了一碗面条。");
Desk.foodStatus = 1;
//做完唤醒顾客
Desk.lock.notifyAll();

}
}
}

}
}
}

顾客(消费者)

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
package wait;

public class Eat extends Thread{

@Override
public void run() {
while (true){
synchronized (Desk.lock) {
if (Desk.count == 0){
break;
}else {

if (Desk.foodStatus == 0) {
try {
Desk.lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
} else {
Desk.count--;
System.out.println("顾客吃了一碗面条。还能再吃"+Desk.count+"碗");
Desk.foodStatus = 0;
//吃完唤醒厨师
Desk.lock.notify();

}
}
}
}
}
}

阻塞队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Block;

import java.util.concurrent.ArrayBlockingQueue;

/**
* 阻塞队列
*/
public class Demo {

public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
Cook cook = new Cook(queue);
Eat eat = new Eat(queue);

cook.start();
eat.start();
}
}

生产者

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
package Block;

import java.util.concurrent.ArrayBlockingQueue;

public class Cook extends Thread{

//阻塞队列
ArrayBlockingQueue<String> queue;

//有参构造
public Cook(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}

@Override
public void run() {
while (true){
try{
//阻塞队列内有锁
queue.put("面条");
//在锁外就会打印混乱
System.out.println("厨师做了一碗面条");
}catch (Exception e){
e.printStackTrace();
}
}
}
}

消费者

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
package Block;

import java.util.concurrent.ArrayBlockingQueue;

public class Eat extends Thread{

//阻塞队列
ArrayBlockingQueue<String> queue;

//有参构造
public Eat(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}

@Override
public void run() {

while (true){
try{
//阻塞队列内有锁
String food = queue.take();
//在锁外就会打印混乱
System.out.println("顾客吃了一碗"+food);
System.out.println("剩余"+queue.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
}

线程池

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
package MyThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* 线程池对象
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {

//获取线程池对象(无上限)
//ExecutorService threadPool = Executors.newCachedThreadPool();
//获取线程池对象(有上限)
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//创建线程对象
//MyThread thread = new MyThread();

//将对象提交给线程池
threadPool.submit(new MyThread());
Thread.sleep(1000);
threadPool.submit(new MyThread());
Thread.sleep(1000);
threadPool.submit(new MyThread());
Thread.sleep(1000);
threadPool.submit(new MyThread());
Thread.sleep(1000);
threadPool.submit(new MyThread());

//关闭线程池
threadPool.shutdown();
}
}


class MyThread implements Runnable {

static int count = 0;

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+count);
}
}

自定义线程池

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
package MyThreadPool;

import java.util.concurrent.*;

/**
* 自定义线程池
* 核心线程满时,再提交任务就会排队
* 核心线程满,队列满时,再提交就会创建临时线程
* 核心线程满,队列满,临时线程满时,就会触发拒绝策略
*
* 拒绝策略:
* AbortPolicy() 默认策略,超出的任务会被丢弃并抛出异常
* CallerRunsPolicy() 丢弃任务,但是不抛出异常
* DiscardOldestPolicy() 抛弃队列中等待最久的任务,然后把当前任务加到队列中
* DiscardPolicy() 调用任务的run()方法绕过线程池直接执行
*/
public class MyPool {

public static void main(String[] args) {

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,//核心线程数量
6,//最大线程数量
60,//最大空闲时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<Runnable>(3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
);
}
}
1
2
3
4
5
6
7
8
9
10
package MyThreadPool;

/**
* 获取可用的处理器数量
*/
public class Processor {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}

线程池计算

CPU密集型计算 – > 最大并行数 + 1

I/O密集型计算 --> 最大并行数* 期望CPU利用率*【(CPU计算时间+CPU等待时间)/CPU计算时间】

补充


Java多线程笔记
https://superlovelace.top/2024/06/07/多线程/
作者
棱境
发布于
2024年6月7日
更新于
2025年2月18日
许可协议