07.多线程基础
# 01.多线程基础
# 1、创建线程
# 1、复写run
- 方法一:从
Thread
派生一个自定义类,然后覆写run()
方法
public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("start new thread!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 2、传Runnable
- 方法二:创建
Thread
实例时,传入一个Runnable
实例
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 3、lambda语法
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});
t.start(); // 启动新线程
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2、线程传参
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
int index = i; // 定义需要传递的参数
Thread thread = new Thread(() -> runWithParameter(index));
thread.start();
}
}
private static void runWithParameter(int index) {
System.out.println("Thread " + index + " is running.");
// 在这里可以使用传入的参数
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3、线程超时退出
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// 模拟一个需要5秒钟才能完成的任务
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Task interrupted!");
return;
}
System.out.println("Task completed!");
}
});
t.start();
// 等待3秒钟
Thread.sleep(3000);
// 如果任务还在执行,则中断任务
if(t.isAlive()) {
t.interrupt();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 4、守护线程
- 主线程退出 守护线程就会退出
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("Daemon thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.setDaemon(true); // 将线程t设置为守护线程
t.start();
Thread.sleep(5000); // 等待5秒钟
System.out.println("Main thread is finished!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 02.线程同步
# 0、线程非安全
- count 加一操作,出现线程非安全问题的演示代码
public class Main {
private static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
count++;
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + count); // count: 99525
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1、synchronized关键字
- synchronized 关键字修饰,表示在调用这些方法的时候,当前线程会获得对象的锁,其他线程只能等待,直到当前线程释放该对象锁
public class Main {
private static int count = 0;
public static synchronized void increment() {
count++;
}
public static synchronized int value() {
return count;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 2、Lock接口
- lock 方法会让当前线程获得锁,如果锁已经被其他线程持有,当前线程就会等待
- unlock 方法会释放当前线程持有的锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static final Lock lock = new ReentrantLock();
private static int count = 0;
public static void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public static int value() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 3、Atomic变量
- incrementAndGet 和 decrementAndGet 方法都是原子操作,可以保证在多线程环境下的线程安全
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private static AtomicInteger count = new AtomicInteger(0);
public static void increment() {
count.incrementAndGet();
}
public void decrement() {
count.decrementAndGet();
}
public static int value() {
return count.get();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 4、synchronized块
- synchronized 代码块会让当前线程获得 lock 对象的锁,其他线程只能等待,直到当前线程释放 lock 对象的锁
public class Main {
private static int count = 0;
private static Object lock = new Object();
public static void increment() {
synchronized (lock) {
count++;
}
}
public void decrement() {
synchronized (lock) {
count--;
}
}
public static int value() {
synchronized (lock) {
return count;
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 03.锁
# 0、死锁演示
- 两个线程分别拥有资源1和资源2,但是它们都需要对方的资源才能继续执行
- 因此,它们都在等待对方释放资源,从而导致死锁,下面代码如法结束
public class Main {
public static void main(String[] args) {
Object resource1 = new Object();
Object resource2 = new Object();
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and 2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 1");
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1 and 2");
}
}
});
thread1.start();
thread2.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
# 1、wait¬ify
wait()
方法用于让一个线程等待,直到另一个线程通知它可以继续执行notify()
方法用于通知等待在某个对象上的线程可以继续执行
public class Main {
public static void main(String[] args) {
final Object lock = new Object(); // 创建一个对象作为锁
Thread thread1 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 1: Acquired lock");
try {
lock.wait(); // 在锁上等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Released lock");
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2: Acquired lock");
lock.notify(); // 唤醒在锁上等待的线程
System.out.println("Thread 2: Released lock");
}
});
thread1.start(); // 启动线程1
try {
Thread.sleep(1000); // 等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start(); // 启动线程2
}
}
/*
Thread 1: Acquired lock
Thread 2: Acquired lock
Thread 2: Released lock
Thread 1: Released 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
33
34
35
36
37
38
39
40
41
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
# 2、ReentrantLock
ReentrantLock
创建了一个锁实例,并在两个线程中使用该锁实现了线程同步- 线程1和线程2分别获取锁、执行一些操作、等待1秒钟,然后释放锁
- 由于锁是可重入的,因此线程可以多次获取同一个锁,而不会被阻塞
- 需要注意的是,和
synchronized
关键字不同,ReentrantLock
必须显式地获取和释放锁
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) {
final ReentrantLock lock = new ReentrantLock(); // 创建一个 ReentrantLock 实例
Thread thread1 = new Thread(() -> {
lock.lock(); // 获取锁
try {
System.out.println("Thread 1: Acquired lock");
Thread.sleep(1000); // 等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
System.out.println("Thread 1: Released lock");
}
});
Thread thread2 = new Thread(() -> {
lock.lock(); // 获取锁
try {
System.out.println("Thread 2: Acquired lock");
Thread.sleep(1000); // 等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
System.out.println("Thread 2: Released lock");
}
});
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
}
}
/*
Thread 1: Acquired lock
Thread 1: Released lock
Thread 2: Acquired lock
Thread 2: Released 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
33
34
35
36
37
38
39
40
41
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
# 3、Condition
Condition
可以让线程在等待某个条件满足时暂停执行,并在条件满足时重新开始执行Condition
接口与wait()
和notify()
方法类似,但提供了更多的灵活性和控制性
package cls;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) {
final ReentrantLock lock = new ReentrantLock(); // 创建一个 ReentrantLock 实例
final Condition condition = lock.newCondition(); // 创建一个 Condition 实例
Thread thread1 = new Thread(() -> {
lock.lock(); // 获取锁
try {
System.out.println("Thread 1: Acquired lock");
condition.await(); // 等待条件满足
System.out.println("Thread 1: Resumed execution");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
System.out.println("Thread 1: Released lock");
}
});
Thread thread2 = new Thread(() -> {
lock.lock(); // 获取锁
try {
System.out.println("Thread 2: Acquired lock");
Thread.sleep(1000); // 等待1秒钟
condition.signal(); // 发出条件满足的信号
System.out.println("Thread 2: Signalled condition");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
System.out.println("Thread 2: Released lock");
}
});
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
}
}
/*
Thread 1: Acquired lock
Thread 2: Acquired lock
Thread 2: Signalled condition
Thread 2: Released lock
Thread 1: Resumed execution
Thread 1: Released 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
# 4、ReentrantLock
ReentrantLock
创建了一个锁实例,并在getCount()
和incrementCount()
方法中使用该锁实现了线程同步getCount()
方法获取共享变量的值,而incrementCount()
方法增加共享变量的值- 由于
ReentrantLock
是可重入的,因此线程可以多次获取同一个锁,而不会被阻塞 - 需要注意的是,和
synchronized
关键字不同,ReentrantLock
必须显式地获取和释放锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static final Lock lock = new ReentrantLock();
private static int count = 0;
public static void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public static int value() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 5、ReadWriteLock
- 由于
ReadWriteLock
允许多个线程同时读取共享资源,而只允许一个线程写入共享资源 - 因此在多个线程同时调用
getCount()
方法时,可以有效地提高并发性能
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main {
private static final ReadWriteLock lock = new ReentrantReadWriteLock(); // 创建一个 ReadWriteLock 实例
private static int count = 0; // 共享的变量
public static int value() {
lock.readLock().lock(); // 获取读锁
try {
return count; // 返回共享变量的值
} finally {
lock.readLock().unlock(); // 释放读锁
}
}
public static void increment() {
lock.writeLock().lock(); // 获取写锁
try {
count++; // 增加共享变量的值
} finally {
lock.writeLock().unlock(); // 释放写锁
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
// count++;
increment();
}
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 6、StampedLock
StampedLock
是 Java 中提供的一种乐观读写锁机制,它可以提供比ReadWriteLock
更高的并发性能StampedLock
支持三种访问模式:读模式、写模式和乐观读模式- 在读模式下,多个线程可以同时读取共享资源,但不能写入共享资源
- 在写模式下,只允许一个线程写入共享资源
- 在乐观读模式下,线程可以读取共享资源,但需要在读取之前获取一个标记
- 然后在读取完成之后检查标记是否仍然有效,如果仍然有效,则表示读取成功,否则需要重试读取
import java.util.concurrent.locks.*;
public class Main {
private static final StampedLock lock = new StampedLock(); // 创建一个 StampedLock 实例
private static int count = 0; // 共享的变量
public static int value() {
long stamp = lock.tryOptimisticRead(); // 尝试获取一个乐观读锁标记
int value = count; // 读取共享变量的值
if (!lock.validate(stamp)) { // 如果标记无效
stamp = lock.readLock(); // 获取一个读锁
try {
value = count; // 重新读取共享变量的值
} finally {
lock.unlockRead(stamp); // 释放读锁
}
}
return value; // 返回共享变量的值
}
public static void increment() {
long stamp = lock.writeLock(); // 获取一个写锁
try {
count++; // 增加共享变量的值
} finally {
lock.unlockWrite(stamp); // 释放写锁
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// count++;
for (int j = 0; j < 1000; j++) increment();
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("count: " + count); // count: 99525
System.out.println("count: " + value());
}
}
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
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
# 7、Semaphore
Semaphore
是 Java 中提供的一种计数信号量机制,它可以控制同时访问共享资源的线程数量Semaphore
维护了一个计数器,表示当前可用的许可证数量- 当一个线程要访问共享资源时,需要先获取一个许可证,如果许可证数量不足,则线程需要等待直到有许可证可用
- 当一个线程使用完共享资源后,需要释放一个许可证,以便其他线程可以访问共享资源
package cls;
import java.util.concurrent.Semaphore;
public class Main {
private final Semaphore semaphore = new Semaphore(2); // 创建一个 Semaphore 实例,初始许可证数量为2
public void accessSharedResource() {
try {
semaphore.acquire(); // 获取一个许可证
System.out.println(Thread.currentThread().getName() + " is accessing shared resource");
Thread.sleep(1000); // 模拟访问共享资源的耗时
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放一个许可证
System.out.println(Thread.currentThread().getName() + " released a permit");
}
}
public static void main(String[] args) {
Main example = new Main();
Thread thread1 = new Thread(() -> example.accessSharedResource(), "Thread 1");
Thread thread2 = new Thread(() -> example.accessSharedResource(), "Thread 2");
Thread thread3 = new Thread(() -> example.accessSharedResource(), "Thread 3");
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
thread3.start(); // 启动线程3
}
}
/*
Thread 2 is accessing shared resource
Thread 1 is accessing shared resource
Thread 3 is accessing shared resource
Thread 1 released a permit
Thread 2 released a permit
Thread 3 released a permit
*/
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
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
上次更新: 2024/5/31 11:18:42