kim.zhang

风在前,无惧!


  • 首页

  • 标签42

  • 分类12

  • 归档94

  • 搜索

发表于 2021-11-21
本文字数: 7.8k 阅读时长 ≈ 7 分钟

1.进程与线程

1.进程是资源分配的最小单位,线程是CPU调度的最小单位。

2.进程可以看作独立应用,线程不能看作独立应用。

3.进程独占内存空间,保存各自的运行状态,相互之间互不干扰且可以互相切换。线程共享进程的内存资源,相互之间切换更加快捷,支持更细粒度的任务控制

4.多进程的程序比多线程的程序更加健壮,但是进程的切换开销比线程大

2. run和start

start()方法的执行流程:

  1. 检查线程状态是不是NEW(线程刚创建未启动的状态),如果不是抛出异常(如果调用多次start方法会抛出IllegalThreadStateException的异常)
  2. 加入线程组
  3. 调用native的start0方法

而run方法只是一个普通的方法。只有调用start方法启动线程才算是真正的启动线程,线程才会有生命周期。

3.Thread类的方法

特别要注意的是run()和exit()都是Thread类的方法。虽然run()在Runnable接口中也有。

此外,Thread类有一个native的notify/notifyAll方法,但是没有wait方法。(注意wait/notify/notifyAll都是Object的方法)

4.线程返回值处理

1.主线程等待法(while循环等待),等待子线程执行完成返回结果

2.使用Thread类的join方法阻塞当前线程,等待子线程执行完成返回结果

3.实现Callbale接口实现,通过FutureTask或线程池submit之后接收的Futre。FutureTask/Future的get()会阻塞直到子线程执行完成返回结果。如果子线程发生异常且没有捕获,get()不再阻塞。

4.1 主线程等待法
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
public class ThreadTest {

public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
new Thread(myThread).start();
// 这里如果是空的方法体,就会陷入死循环。如果加入sleep,就轮询直到子线程返回结果
while(myThread.getStr() == null) {
Thread.sleep(100);
};
System.out.println("主线程接收到结果,result is " + myThread.getStr());
}

}

class MyThread implements Runnable {

private String str;

/**
* Runnable的run方法没有返回值,只能通过成员变量返回
*/
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.str = "i am result";
}

public String getStr() {
return str;
}
}
4.2 使用join()

使用4.1中的MyThread,改变main方法:

1
2
3
4
5
6
7
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
Thread t = new Thread(myThread);
t.start();
t.join();
System.out.println("主线程接收到结果,result is " + myThread.getStr());
}
4.3 通过FutureTask接收返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ThreadTest {

public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new MyThread());
// 启动线程,FutureTask实现了Runnable接口
new Thread(futureTask).start();
if(!futureTask.isDone()) {
System.out.println("正在等待子线程执行完毕...");
}
System.out.println("主线程接收到结果,result is " + futureTask.get());
}

}

class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "i am result";
}
}
4.4 通过线程池和Futrue接收返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ThreadTest {

public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(new MyThread());
if(!future.isDone()) {
System.out.println("正在等待子线程执行完毕...");
}
System.out.println("主线程接收到结果,result is " + future.get());
}

}

class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "i am result";
}
}

5.notify/notifyAll

锁池:两个线程竞争同一把锁,没有竞争到锁的线程将进入锁池。锁池可以竞争锁。

等待池:在等待池的线程等待着被唤醒进入锁池竞争锁。

1.notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ThreadTest {

public static void main(String[] args) {
Object lock = new Object();

Runnable waitRunnable = () -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "正在等待..." + new Date());
try {
// 当前线程进入等待池,等待notify/notifyAll通知进入锁池竞争锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "获得锁..." + new Date());
}
};

Runnable notifyRunnable = () -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "正在唤醒线程...");
// 通知等待池中所有锁进入锁池去竞争锁
lock.notifyAll();
// 随机选择等待池中的一个锁进入锁池去竞争锁
//lock.notify();
}
};

Thread t1 = new Thread(waitRunnable);
Thread t2 = new Thread(waitRunnable);
Thread t3 = new Thread(waitRunnable);
Thread t4 = new Thread(notifyRunnable);

t1.start();
t2.start();
t3.start();

try {
// 确保t1,t2,t3先启动
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
t4.start();
}
}

6.停止线程

错误的停止方法:

1.stop:stop非常粗暴,被stop的线程会释放所有的锁并停止线程执行,即使它并没有执行完成。stop不能完成一个完整单位的停止。

2.resume:resume会将当前的线程挂起,但并没有释放锁。这有可能会导致其他需要这把锁的线程发生死锁。

3.volatile修饰的成员变量控制停止:在某些情况下,volatile修饰的成员变量去控制线程的停止是可行的。但在线程阻塞的时候,线程有可能检测不到成员变量的改变,导致线程停止失败。

例子中,生产者往queue中生产数据,消费者从queue中消费数据。当消费者消费完成后,希望生产者停止线程。

使用volatile修饰的canceled变量控制线程的停止,当生产者的线程阻塞时,无法检测到外界canceled变量的改变,而无法正确停止线程。

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
public class ThreadTest {

public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
Producer producer = new Producer(queue);
Thread producerThread = new Thread(producer);
Thread customerThread = new Thread(new Customer(queue));
producerThread.start();
// 生产者先生产1s
Thread.sleep(1000);

customerThread.start();
// 1s后消费者不再消费
Thread.sleep(1000);

// 停止生产者
producer.cancel();
System.out.println("消费者不再消费,请求生产者停止生产...");
}
}

class Producer implements Runnable {

private BlockingQueue<Integer> queue;

private boolean canceled = false;

public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}

@Override
public void run() {

try {
int i = 0;
while (i <= 10000 && !canceled) {
if (i % 100 == 0) {
// 当消费者cancel的时候,生产者阻塞在了这里,检测不到canceled的改变,也就导致了不能正确停止线程
queue.put(i);
System.out.println("生产者生产" + i);
}
i++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("生产者停止生产...");
}
}

public void cancel() {
this.canceled = true;
}
}

class Customer implements Runnable {

private BlockingQueue<Integer> queue;

public Customer(BlockingQueue<Integer> queue) {
this.queue = queue;
}

@Override
public void run() {
while (needMore()) {
try {
Integer ele = queue.take();
System.out.println("消费者消费" + ele);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private boolean needMore() {
if (Math.random() > 0.75) {
return true;
}
return false;
}
}

正确的停止方法:

使用Thread.interrupt停止线程

将上面的例子修改为使用interrupt来停止线程,当调用interrupt方法时,isInterrupted方法可以检测到线程的中断。

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
public class ThreadTest {

public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
Producer producer = new Producer(queue);
Thread producerThread = new Thread(producer);
Thread customerThread = new Thread(new Customer(queue));
producerThread.start();
// 生产者先生产1s
Thread.sleep(1000);

customerThread.start();
// 1s后消费者不再消费
Thread.sleep(1000);

// 停止生产者
producerThread.interrupt();
System.out.println("消费者不再消费,请求生产者停止生产...");
}
}

class Producer implements Runnable {

private BlockingQueue<Integer> queue;

private boolean canceled = false;

public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}

@Override
public void run() {

try {
int i = 0;
// isInterrupted()检测线程是否中断
while (i <= 10000 && !Thread.currentThread().isInterrupted()) {
if (i % 100 == 0) {
queue.put(i);
System.out.println("生产者生产" + i);
}
i++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("生产者停止生产...");
}
}

public void cancel() {
this.canceled = true;
}
}

class Customer implements Runnable {

private BlockingQueue<Integer> queue;

public Customer(BlockingQueue<Integer> queue) {
this.queue = queue;
}

@Override
public void run() {
while (needMore()) {
try {
Integer ele = queue.take();
System.out.println("消费者消费" + ele);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private boolean needMore() {
if (Math.random() > 0.75) {
return true;
}
return false;
}
}
一毛也是爱~
Kim.Zhang 微信支付

微信支付

  • 文章目录
  • 站点概览
Kim.Zhang

Kim.Zhang

且行且珍惜
94 日志
12 分类
42 标签
E-Mail Weibo
  1. 1. 1.进程与线程
  2. 2. 2. run和start
  3. 3. 3.Thread类的方法
  4. 4. 4.线程返回值处理
    1. 4.1. 4.1 主线程等待法
    2. 4.2. 4.2 使用join()
    3. 4.3. 4.3 通过FutureTask接收返回值
    4. 4.4. 4.4 通过线程池和Futrue接收返回值
  5. 5. 5.notify/notifyAll
  6. 6. 6.停止线程
粵ICP备19091267号 © 2019 – 2022 Kim.Zhang | 629k | 9:32
本站总访问量 4 次 | 有 309 人看我的博客啦 |
博客全站共176.7k字
载入天数...载入时分秒...
0%