【598-650】多线程
2022-02-05 11:40:00 # JavaSE

概述

进程和线程是什么

进程是一个应用程序,可启动多个线程
线程是进程中的执行单元

DOS输入 java HelloWorld 后发生了什么

  1. 先启动JVM,JVM就是一个进程
  2. JVM再启动一个主线程调用main方法
  3. 同时启动一个垃圾回收线程负责看护,回收垃圾
  4. 此时有两个线程并发

进程和线程是什么关系

  1. 两进程之间内存独立不共享
  2. 两线程之间堆内存和方法区内存共享,栈内存独立不共享,多个线程启动多个栈,就是多线程并发,可以提高效率
  3. main方法结束后只是主线程结束了,其他线程可能还在压栈弹栈

单核CPU不能真正做到多线程并发

  1. 什么是真正的多线程并发?
    t1线程执行t1的
    t2线程执行t2的
    t1不会影响t2,t2也不会影响t1。
  2. 单核的CPU表示只有一个大脑:不能够做到真正的多线程并发,但是可以做到给人一种”多线程并发”的感觉,因为人类反应速度较慢

实现线程的方式

继承java.lang.Thread,重写run()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class _603_实现线程的第一种方式 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
// 启动线程 在JVM中开辟一个新的栈
// 开完栈空间此行代码结束
// 栈中run方法在栈底,main方法在主栈底
myThread.start();
// 这儿的代码还在main主线程中
for (int i = 0; i < 1000; i++) {
System.out.println("主线程 -- > " + i);
}
}
}

class MyThread extends Thread{
@Override
public void run() {
System.out.println("MyThread run begin~");
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程 -- > " + i);
}
}
}

实现java.lang.Runnable接口,实现run()

面向接口编程,使用较多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class _605_实现线程的第二种方法 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程 -- > " + i);
}
}
}

// 这是一个线程类,是一个可运行的类,还不是一个线程
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("MyThread run begin~");
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程 -- > " + i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class _606_采用匿名内部类方式 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程 -- > " + i);
}
}
});
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程 -- > " + i);
}
}
}

线程生命周期

这儿因该有张图…

线程相关方法

public final synchronized void setName(String name)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class _609_获取线程名字 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
// 设置线程名字
myThread2.setName("线程");
// 获取线程名字
System.out.println(myThread2.getName());
// 启动线程
myThread2.start();
}
}

class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程 -> " + i);
}
}
}

public final String getName()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class _609_获取线程名字 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
// 设置线程名字
myThread2.setName("线程");
// 获取线程名字
System.out.println(myThread2.getName());
// 启动线程
myThread2.start();
}
}

class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程 -> " + i);
}
}
}

public static native Thread currentThread();

出现在哪就是获取当前线程对象

1
2
3
4
5
6
7
8
9
public class _610_获取当前线程对象 {
public static void main(String[] args) {
// 这个方法出现在main方法中,所以当前线程就是主线程
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName()); // main
}
}


public static native void sleep(long millis) throws InterruptedException

1
2
3
4
5
6
7
8
9
10
11
12
13
public class _611_线程sleep方法 {
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("五秒之后的输出~");
}
}


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
public class _612_sleep方法的面试题 {
public static void main(String[] args) {
MyThread3 myThread3 = new MyThread3();
myThread3.start();

try {
myThread3.sleep(5000);
// 这执行的时候会转化为 -> Thread.sleep(5000)
// 也就是当前main线程进入休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
// 结果是 "HELLO WORLD~" 在五秒后输出
System.out.println("HELLO WORLD~");
}
}

class MyThread3 extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}

public void 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class _613_终止线程的睡眠 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable2());
thread.start();

// 希望 5 秒之后 thread 线程醒来
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// 终止线程睡眠
thread.interrupt();
// 在第 8 行报异常,进入catch然后结束醒来
// 依靠异常机制
/*
Thread-0-> begin
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at MyRunnable2.run(_613_终止线程的睡眠.java:25)
at java.lang.Thread.run(Thread.java:748)
Thread-0-> end
*/
}
}

class MyRunnable2 implements Runnable{
// 子类不能父类抛出更多异常
// 所以run()只能try catch
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-> begin");
try {
Thread.sleep(100000000);
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-> end");
}
}

public final void stop()

终止一个线程的执行
相当于突然断点,内存中的东西没有保存,不建议使用

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
public class _614_强行终止线程的执行 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable3());
// 5 sec后终止线程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.stop();
System.out.println("==========");
}
}

class MyRunnable3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-> " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException 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
public class _615_合理的终止一个线程的执行 {
public static void main(String[] args) {
MyRunnable4 myRunnable4 = new MyRunnable4();
Thread thread = new Thread(myRunnable4);
//
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myRunnable4.run = false;
/*
Thread-0->0
Thread-0->1
Thread-0->2
Thread-0->3
Thread-0->4
*/
}
}

class MyRunnable4 implements Runnable{
boolean run = true;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(run){
System.out.println(Thread.currentThread().getName() + "->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
// 这儿可以进行一些保存操作...

return;
}
}
}
}

线程调度

线程调度模型

  1. 抢占式调度模型:按照线程的优先级来看,优先级越高,强盗CPU时间片的概率就高一些,java采用的就是这种
  2. 均分式调度模型:平均分配CPU时间片,每个线程占用时间片时间长度相同

线程调度方法

public final int getPriority()

线程最低优先级是1,最高是10,默认是5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class _618_线程优先级 {
public static void main(String[] args) {
System.out.println("线程最高优先级" + Thread.MAX_PRIORITY);
System.out.println("线程最低优先级" + Thread.MIN_PRIORITY);
System.out.println("线程默认优先级" + Thread.NORM_PRIORITY);

Thread thread = Thread.currentThread();
System.out.println(thread.getPriority()); // 5

}
}



public final void setPriority(int newPriority)

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
public class _618_线程优先级 {
public static void main(String[] args) {
System.out.println("线程最高优先级" + Thread.MAX_PRIORITY);
System.out.println("线程最低优先级" + Thread.MIN_PRIORITY);
System.out.println("线程默认优先级" + Thread.NORM_PRIORITY);

Thread thread = Thread.currentThread();
System.out.println(thread.getPriority()); // 5
// 设置优先级
thread.setPriority(1);

Thread t = new Thread(new MyRunnable5());
t.start();

for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}

class MyRunnable5 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}


public static native void yield()

让位,当前线程暂停,回到就绪状态,让给其它线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class _619_线程让位 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable6());
t.start();

for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}

class MyRunnable6 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
if(i%10 == 0){
// 让位
Thread.yield();
}
}
}
}

public final void join() throws InterruptedException

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
public class _620_线程合并 {
public static void main(String[] args) {
System.out.println("main begin~");
Thread t = new Thread(new MyRunnable7());
t.start();

// join() 两个栈之间有等待关系
// t线程执行直到结束
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("main over~");
}
}

class MyRunnable7 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}

线程安全

什么情况出现线程安全问题

  1. 多线程并发
  2. 有共享数据
  3. 共享数据有修改的行为
    (这儿应该有张图…)

如何解决线程安全问题

线程排队执行,这种机制被称为:线程同步机制。也就是线程不能并发,必须排队执行,为了安全牺牲效率。

同步和异步的理解

  1. 异步编程模型:线程t1和t2各自执行各自的,就是多线程并发
  2. 同步编程模型:线程t1和t2在执行的过程中发生等待关系,线程排队执行

定义账户类模拟两线程取钱 synchronized

使用 synchronized 实现线程同步
在 synchronized 代码部分中:

  1. 假设t1和t2线程并发,开始执行以下代码的时候,肯定有一个先一个后。
  2. 假设t1先执行了,遇到了synchronized,这个时候自动找后面共享对象”的对象锁找到之后,并占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一疸都是占有这把锁的。直到同步代码块代码结束,这把锁才会释故。
  3. 假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有后面共享对象的这把锁,结果这把锁被t1占有,t2只能在同步代码块外面等待t1的结束,查到t1把同步代码块执行结束了,,t1会归还这把锁,此时t2终于等到这把锁,然后t2占有这把锁之后,进入同步代码块执行程序。
  4. 对 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
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class _625_账户类的定义 {
public static void main(String[] args) {
// 创建一个账户
Account act = new Account("act-001", 10000);

// 创建两个线程
AccountThread t1 = new AccountThread(act);
AccountThread t2 = new AccountThread(act);

// 设置线程名
t1.setName("t1");
t2.setName("t2");

// 两个线程开始执行run()
t1.start();
t2.start();

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("账户最终余额:" + act.getBalance());
}
}

class AccountThread extends Thread{

private Account act;

public AccountThread(Account o){
this.act = o;
}

// 表示取款操作
@Override
public void run() {
// t1 t2 并发这个方法 不考虑安全问题就会出现问题
// 于是使用 synchronized 同步代码块

// 假设取款5000
double money = 5000;
// 取款并更新余额
act.withdraw(money);
}

public Account getAct() {
return act;
}

public void setAct(Account act) {
this.act = act;
}
}

class Account{
private String actno;
private double balance;

public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}

// 取款
public void withdraw(double money){

// 以下这几行代码一定是线程排队执行!!!
/*
假设有t1~5,5个线程
希望t1~3排队,t4~5不排队
于是在 synchronized () 中写入 共享对象

由于这里账户是共享对象,那么写入 this
*/
synchronized (this){
// 一下是线程同步代码块~

double before = this.getBalance();
double after = before - money;

// 模拟网络延迟
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }

this.setBalance(after);

System.out.println(Thread.currentThread().getName() + "操作" + "余额:" + this.getBalance());
}
}

public String getActno() {
return actno;
}

public void setActno(String actno) {
this.actno = actno;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}
}

synchronized 可以出现在实例方法上,只能锁this,不灵活,但是省代码
表示整个方法体都需要同步

1
public synchronized void withdraw(double money)

哪些变量有线程安全问题

  1. 实例变量:在堆中
  2. 静态变量:在方法区中
  3. 局部变量:在栈中

以上三大变量中,局部变量不会出现线程安全问题
不满足线程安全中共享数据的条件
栈可以有很多个,局部变量在栈中,而堆和方法区只有一个
synchronized 出现在某类中静态方法上会锁类锁

synchronized 的三种写法

  1. 同步代码块
    1
    2
    3
    synchronized(线程同步对象){
    // 同步代码块
    }
  2. 在实例方法上使用synchronized
    表示共享对象一定是this,并且同步代码块是整个方法体
  3. 在静态方法上使用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
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
public class _638_死锁概述 {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();

MyThread1 t1 = new MyThread1(o1, o2);
MyThread2 t2 = new MyThread2(o1, o2);

t1.start();
t2.start();
}
}

class MyThread1 extends Thread{
Object o1;
Object o2;

public MyThread1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}

@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){

}
}
System.out.println(Thread.currentThread().getName() + "run()执行结束");
}
}

class MyThread2 extends Thread{
Object o1;
Object o2;

public MyThread2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}

@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){

}
}
System.out.println(Thread.currentThread().getName() + "run()执行结束");
}
}



开发中怎么解决线程安全问题

  1. 尽量使用局部变量代替“实例变量和静态变量”
  2. 如果必须是实例变量,可以考虑创建多个对象,这样实例变量的内存就不共享了
  3. 不能用局部变量,也不能创建多个对象,那就只能 synchronized 了

守护线程

  1. 线程分为两大类:用户线程、守护线程(后台线程)
  2. 守护线程一般是个死循环,所有的用户线程只要结束,守护线程自动结束
  3. 主线程main方法是一个用户线程

实现守护线程

main用户线程结束后,守护线程就结束了

1
public final void setDaemon(boolean on)
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
public class _642_实现守护线程 {
public static void main(String[] args) {
BakDataThread t = new BakDataThread();
t.setName("数据备份线程");
// 设置为守护线程
t.setDaemon(true);

t.start();

for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}

class BakDataThread extends Thread{
@Override
public void run() {
int i = 0;
while (true) {
System.out.println(Thread.currentThread().getName() + "->" + (++i));
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

定时器

  1. 间隔的定时间,执行特定程序
  2. 可以使用sleep(),比较low
  3. 在java.util.Timer实现了定时器,可以直接拿来用
  4. 在实际开发中,使用较多的是Spring框架中提供的SpringTask框架

实现定时器

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
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class _644_实现定时器 {
public static void main(String[] args) throws ParseException {
Timer timer = new Timer();
// Timer timer = new Timer(true); // 守护线程

// 指定定时任务

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2022-02-07 17:42:00");

Date date1 = new Date(System.currentTimeMillis() + 1000 * 2);

timer.schedule(new LogTimerTask(), date1, 1000 * 10);

/*
2022-02-07 17:45:32完成了一次数据备份
2022-02-07 17:45:42完成了一次数据备份
...
*/
}
}

class LogTimerTask extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(new Date());
System.out.println(s + "完成了一次数据备份");
}
}

实现线程的第三种方式

实现Callable接口。(JDK8新特性)
可以获得线程的返回值
缺点是获取执行结果的时候效率较低

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
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class _645_实现线程的第三种方式 {
public static void main(String[] args) {
FutureTask<Object> task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
// 模拟执行
System.out.println("call method begin~");
Thread.sleep(1000 * 10);
System.out.println("call method over~");

int a = 100;
int b = 200;

return a + b;
}
});

Thread t = new Thread(task);
t.start();

// 在主线程中如何获取t线程的返回值
// 这里 get 方法会使得 main 方法受阻
// 等待 task.run() 执行结束
Object o = null;
try {
o = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

// 获得返回值
System.out.println(o);

}
}

wait和notify方法

  1. wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法
  2. wait和notify方法建立在synchronized线程同步的基础之上

wait() 的作用

1
2
Object o = new Object();
o.wait();

表示:让正在o对象上活动的线程进入等待状态,并且释放之前占有的o对象的锁,无期限等待直到被唤醒

notify() 的作用

1
2
Object o = new Object();
o.notify();

表示:让正在o对象上活动的随机一个线程从等待状态唤醒,o.notify()只会通知,不会释放之前占有的o对象的锁

notifyAll() 作用

表示:唤醒在o对象上等待的所有线程

生产者和消费者模式

(这儿应该有张图… )

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class _648_实现生产者和消费者模式 {
public static void main(String[] args) {
// 创建1个仓库对象 仓库容量为 1
List list = new ArrayList();

// 创建一个生产者线程
Thread t1 = new Thread(new Producer(list));
t1.setName("生产者");
// 创建一个消费者线程
Thread t2 = new Thread(new Consumer(list));
t2.setName("消费者");

//
t1.start();
t2.start();
}
}

class Producer implements Runnable{
// 仓库
private List list;

public Producer(List list) {
this.list = list;
}

@Override
public void run() {
while (true){
// 给仓库对象list加锁
synchronized (list){
if (list.size() > 0){
try {
// 进入等待状态,并且释放Producer占有的list集合的锁
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
// 给仓库加东西
double o = Math.random();
list.add(o);
System.out.println(Thread.currentThread().getName() + "生产->" + o);

// 于是唤醒消费者消费
list.notify();
}
}
}
}
}

class Consumer implements Runnable{
// 仓库
private List list;

public Consumer(List list) {
this.list = list;
}

@Override
public void run() {
while (true) {
synchronized (list){
// 仓库空了
if(list.size() == 0){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
// 拿仓库中的东西
Object o = list.remove(0);
System.out.println(Thread.currentThread().getName() + "消费->" + o);

// 于是唤醒生产者
list.notify();
}
}
}
}
}

线程作业

ta,tb交替输出

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class _650_布置线程作业实现交替输出 {
public static void main(String[] args) {
Sz sz = new Sz(1);
ThreadA ta = new ThreadA(sz);
ThreadB tb = new ThreadB(sz);

ta.setName("ta");
tb.setName("tb");

ta.start();
tb.start();
/*
ta->1
tb->2
ta->3
tb->4
ta->5
...
*/
}
}

class Sz{
int cnt;

public Sz(int cnt) {
this.cnt = cnt;
}
}

class ThreadA extends Thread{

Sz sz;

public ThreadA(Sz sz) {
this.sz = sz;
}

@Override
public void run() {
while (true) {
synchronized (sz){
if (sz.cnt%2 != 1){
try {
sz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + sz.cnt);
sz.cnt++;
sz.notify();
}
}
}
}
}

class ThreadB extends Thread{

Sz sz;

public ThreadB(Sz sz) {
this.sz = sz;
}

@Override
public void run() {
while (true) {
synchronized (sz){
if(sz.cnt%2 != 0){
try {
sz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + sz.cnt);
sz.cnt++;
sz.notify();
}
}
}
}
}
Prev
2022-02-05 11:40:00 # JavaSE
Next