廖雪峰java 进程和线程

廖雪峰java 进程和线程

多线程基础

1.进程包含线程,且至少包含一个线程

2.可用多进程、多线程、多进程+多线程3钟方式实现多任务

3.一个应用程序可以包含多个进程和多个线程

4.进程的优点:稳定性好 一个进程崩溃不会影响其它进程 缺点:创建一个进程比创建一个线程开销大 进程间通信比线程间通信时间久

线程缺点:一个线程崩溃会影响其它所有线程,进而整个进程

5.单核、多核都可以通过轮流执行任务实现多任务同时执行

6.一个java程序实际上就是一个jvm进程,由一个主线程来执行main()方法,main()方法内部又可以启动多个其它的线程

7.对于大多数Java程序来说,我们说多任务,实际上是说如何使用多线程实现多任务、

8.多线程和单线程相比,需要共享和同步数据。因此,多线程编程的复杂度高,调试更困难

9.操作系统调度的最小任务单位其实不是进程,而是线程。

10.如何调度线程完全由操作系统决定,程序自己不能决定什么时候执行,以及执行多长时间。

创建新线程

自定义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

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("This is a new Thread!");

}

}

传入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

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("This is a new Thread!");

}

}

使用lambda语法精简

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class Main{

public static void main(String[] args){

Thread t = new Thread(()->{"This is a new Thread!"};);

t.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

public class Main {

public static void main(String[] args) {

System.out.println("main Thread start!");

Thread t1 = new Thread(){

public void run(){

System.out.println("t1 Thread start!");

try{

Thread.sleep(20);

}catch(InterruptedException e){}

System.out.println("t1 Thread end!");

}



}

t1.start();

try{

Thread.sleep(20);

}catch(InterruptedException e){}

System.out.println("main Thread end!");

}

}

1
2
3

{ ... }:表示定义了一个Thread类的 匿名子类,并在其中重写了run()方法。

设置线程优先级

1
2
3
4
5
6
7

Thread.setPriority(int n) // 1~10, 默认值5



操作系统对高优先级线程可能调度更频繁,但我们决不能通过设置优先级来确保高优先级的线程一定会先执行。

注意

直接调用线程实例的run方法不会启动线程,相当于调用实例方法

一个线程对象只能调用一次start()方法

线程状态

1
2
3
4
5
6
7
8
9
10
11
12
13

New:新创建的线程,尚未执行

Runnabble:运行中的线程,正在执行run()方法的Java代码

Blocked:运行中的线程,因为某些操作被阻塞而挂起

Waiting:运行中的线程,因为某些操作在等待中

Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待

Terminated:线程已终止,因为run()方法执行完毕

1
2
3
4
5
6
7
8
9
10
11

线程终止的原因:

线程正常终止:run()方法执行到return语句返回;

线程意外终止:run()方法因为未捕获的异常导致线程终止;

对某个线程的Thread实例调用stop()方法强制终止(强烈不推荐使用)



等待 join()

一个线程还可以等待另一个线程直到其运行结束

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 Main {

public static void main(String[] args) throws InterruptedException {

Thread t = new Thread(() -> {

System.out.println("hello");

});

System.out.println("start");

t.start(); // 启动t线程

t.join(); // 此处main线程会等待t结束

System.out.println("end");

}

}

1
2
3

join(long)的重载方法也可以指定一个等待时间,超过等待时间后就不再继续等待

中断线程

intterupt()

中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行

如果线程处于等待状态,例如,t.join()会让线程进入等待状态,如下例对t线程调用intterupted,join()方法会立刻抛出InterruptedException,因此,目标线程只要捕获到join()方法抛出的InterruptedException,就说明有其他线程对其调用了interrupt()方法

t结束之前如果不对hello调用intterupted,hello进程会继续运行

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

public class Main {

public static void main(String[] args) throws InterruptedException {

System.out.println("main thread start!");

Thread t = new myThread();

t.start();

Thread.sleep(10000);

t.interrupt();

System.out.println("main thread end!");

}

}



class myThread() extends Thread{

@Override

public void run(){

Thread hello = new helloThread();

hello.start();

try{

hello.join();

}catch(interruptedException){

System.out.println("myThread intterupted!");

}

hello.interrupt();

}

}



class helloThread() extends Thread{

@Override

public void run(){

int n = 0;

while(!isInterrupted){

n++;

System.out.println("hello"+n);

}

System.out.println("hello"+n+"end!");

}

}

设置标志位

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 Main {

public static void main(String[] args) throws InterruptedException {

System.out.println("main thread start!");

Thread t = new myThread();

t.start();

t.running=false;

}

}



class myThread() extends Thread{

public vlatile boolean running = true;

@Override

public void run(){

while(running){

}

}

}

1
2
3
4
5
6
7
8
9
10
11
12
13

线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。线程间共享变量每个线程使用时都是使用的副本。

因此,volatile关键字的目的是告诉虚拟机:

• 每次访问变量时,总是获取主内存的最新值;

• 每次修改变量后,立刻回写到主内存。



volatile关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值。

public class Counter{

public static void test(int i){

    syncronized(Counter.class){

        count+=i;

    }

} 

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15



~~~java

public class Counter{

public syncronized static void test(int i){

count+=i;

}

}

while(true){

                   try{

                      String s = q.getTask();

                      System.out.println("execute task: " + s);

                   }catch(InterruptedException e){

                       return;

                   }

               } 

            }

        };

        execThread.start();

        ts.add(execThread);

        Thread.sleep(100);

        for(var execThread:ts){

            t.interrupt();

        }

    }

}

}

class taskQueue{

Queue queue = new LinkedList<>();

public syncronized void addTask(String s){

    this.queue.add(s);

    this.notifiAll();

}

public syncronized String getTask() throws InterruptedException {

    while(queue.isEmpty()){

        this.wait();

    }   

    return queue.remove();

}

}