Java并发编程(01)—— 常用api解析

以前其实写过几篇和多线程有关的东西,但感觉都不成体系。随着对并发编程的深入,想系统的整理一下所学所想。一是可以和大家分享,二是可以给自己查漏补缺。先从最基础的常用api聊起。

序言

先说一下我认为学习多线程常用到的api

  1. java.lang.Object#wait(long):是一个重载方法有多种形式,但最终调用的都是java.lang.Object#wait(long),调用该方法代表着挂起当前线程,释放掉持有的锁,直到有其他线程调用notifynotifyAll方法唤醒该线程,才能再次争抢锁。

注:wait方法必须在synchronized代码块中调用否则会抛出java.lang.IllegalMonitorStateException异常

  1. java.lang.Object#notify:随机唤起一个调用wait方法而等待的线程

  2. java.lang.Object#notifyAll:唤起全部应调用wait方法而等待的线程

    注:若为特殊情况推荐使用notifyAll方法唤醒等待线程,因为notify随机唤醒的特性可能会导致线程饥饿,导致部分线程一直无法获取到锁

  3. java.lang.Thread#sleep(long):使当前线程休眠指定的毫秒数,不会释放锁

  4. java.lang.Thread#join(long):本质上调用的就是wait方法,使当前线程等待

  5. java.lang.Thread#yield:向调度器表示当前线程愿意让出当前CPU的执行权,但调度器可以忽略这点。

常见的生产者消费者模型

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
67
68
package com.yuehua.concurrent.demo;

import java.util.PriorityQueue;
import java.util.Queue;

/**
*
* 简单的生产者-消费者模型
*
* @author yuehua
* @version v1.0
* @since 2021/1/6 13:54
*/
public class WaitDemo {

private static final Queue<Integer> QUEUE = new PriorityQueue<>();

public static void main(String[] args) {
new Thread(WaitDemo::poll).start();
new Thread(WaitDemo::offer).start();
}

/**
* 生产数据
*/
private static void offer() {
while (true) {
synchronized (QUEUE) {
while (QUEUE.size() < 10) {
System.out.println("生产数据");
QUEUE.offer(0);
QUEUE.notifyAll();
}
try {
QUEUE.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

/**
* 消费数据
*/
private static void poll(){
while (true) {
synchronized (QUEUE) {
while (QUEUE.size() == 0) {
try {
QUEUE.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
QUEUE.notifyAll();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费数据 "+ Thread.currentThread().getName());
QUEUE.poll();
}
}
}

}

运行以上代码我们会发现有一个问题,那就是生产者必须等待消费者消费完所有数据才能继续生产,消费者也是同理。理想情况下应该是只要队列不满生产者就可以继续生产,只要队列不空消费者就可以继续消费