A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

1 序言

最近面试期间,发现自己的并发知识比较薄弱,准备写一个关于并发的系列学习笔记。

2 Thread类主要方法

相信Thread类大家并不陌生,在创建线程的时候几乎都会用到它。下面咱们聊一聊Thread类中的主要方法。

2.1 start方法

开启一个线程的方法。

注意:继承Thread类,创建该对象后,只有调用start方法才会开启一个线程,调用run方法不会开启一个线程。

2.2 run方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

2.3 sleep方法

sleep的两个重载的方法:

public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
1
2
sleep方法的作用:使当前线程休眠指定毫秒数,但该线程并不会释放锁(The thread does not lose ownership of any monitors.)。

sleep方法需注意如下:

线程sleep休眠时,线程会交出CPU,让CPU去执行其他的任务。
线程在休眠时,不会释放锁
线程在休眠时,可以被打断(interrupt),并抛出InterruptedException异常
代码示例:

public class ThreadTest {

    public static void main(String[] args) {

        ThreadTest test = new ThreadTest();
        MyThread myThread = test.new MyThread();

        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);


        thread1.start();
        thread2.start();

    }

    public class MyThread implements Runnable {

        @Override
        public void run() {

            synchronized (this) {
                System.out.println("子线程:" + Thread.currentThread().getName() + "入synchronised块,等待3秒...");

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("子线程:" + Thread.currentThread().getName() + "出synchronised块");
            }

        }
    }

}

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
执行结果:

子线程:Thread-0入synchronised块,等待3秒...
子线程:Thread-0出synchronised块
子线程:Thread-1入synchronised块,等待3秒...
子线程:Thread-1出synchronised块
1
2
3
4
可以看到,Thread-0在sleep过程中,并没有释放锁,因而Thread-1不能进入同步语句块,只有当Thread-0的sleep时间结束后,Thread-1才能拿到锁进入同步语句块。

2.5 yield方法

先看一下官方的介绍:

A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.
翻译为:

向调度程序提示当前线程是否愿意让出其当前使用的处理器。调度程序可以忽略此提示。
yield翻译为:让出、退让。

yield方法需要注意:

yield方法会让出当前线程的CPU执行权,让其他线程去获取CPU执行权
yield方法跟sleep方法类似,也不会释放锁
yield方法不能控制具体交出CPU的时间
调用yield方法不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待时机重新获取CPU执行权
代码示例:

public class ThreadTest {

    public static void main(String[] args) {

        ThreadTest test = new ThreadTest();
        MyThread myThread = test.new MyThread();

        Thread thread0 = new Thread(myThread);
        Thread thread1 = new Thread(myThread);

        thread0.start();
        thread1.start();
        System.out.println(Thread.currentThread().getName() + ":执行完毕");
    }

    public class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":开始执行");

            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":循环" + "第"+ i + "次");
                Thread.yield();
            }

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

    }
}
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
运行结果:

Thread-0:开始执行
Thread-0:循环第0次
Thread-0:循环第1次
Thread-0:循环第2次
Thread-1:开始执行
Thread-1:循环第0次
main:执行完毕
Thread-0:循环第3次
Thread-1:循环第1次
Thread-0:循环第4次
Thread-1:循环第2次
Thread-0:循环第5次
Thread-1:循环第3次
Thread-0:循环第6次
Thread-1:循环第4次
Thread-0:循环第7次
Thread-1:循环第5次
Thread-0:循环第8次
Thread-1:循环第6次
Thread-0:循环第9次
Thread-1:循环第7次
Thread-0:执行结束
Thread-1:循环第8次
Thread-1:循环第9次
Thread-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
可以看到Thread-0线程和Thread-1线程几乎是在交替进行的

当给循环加上同步语句块后:

public class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":开始执行");

                        //加上同步语句块
            synchronized(this){
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ":循环" + "第"+ i + "次");
                    Thread.yield();
                }
            }

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

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行结果:

Thread-0:开始执行
main:执行完毕
Thread-1:开始执行
Thread-0:循环第0次
Thread-0:循环第1次
Thread-0:循环第2次
Thread-0:循环第3次
Thread-0:循环第4次
Thread-0:循环第5次
Thread-0:循环第6次
Thread-0:循环第7次
Thread-0:循环第8次
Thread-0:循环第9次
Thread-0:执行结束
Thread-1:循环第0次
Thread-1:循环第1次
Thread-1:循环第2次
Thread-1:循环第3次
Thread-1:循环第4次
Thread-1:循环第5次
Thread-1:循环第6次
Thread-1:循环第7次
Thread-1:循环第8次
Thread-1:循环第9次
Thread-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
可以看到加上同步语句块后,Thread-0虽然让出了CPU执行权,但没有释放锁,Thread-1仍然不能进入同步语句块。其实可以理解为这时候调度程序已经忽略yield方法。

2.6 join方法

看一下官方的介绍:

Waits for this thread to die.
翻译:

等待这个线程死亡
这个句子缺少主语,不便于理解。下面结合具体的测试代码去理解:

public class ThreadTest {

    public static void main(String[] args) {

        ThreadTest test = new ThreadTest();
        MyThread myThread = test.new MyThread();

        Thread thread0 = new Thread(myThread);

        thread0.start();

        try {
            System.out.println(Thread.currentThread().getName() + "---join前");
            thread0.join();
            System.out.println(Thread.currentThread().getName() + "---join后");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "开始执行");

            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "循环中...");
            }
            System.out.println(Thread.currentThread().getName() + "执行结束");
        }

    }
}

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
执行结果:

Thread-0开始执行
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
main---join前
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
Thread-0循环中...
Thread-0执行结束
main---join后
1
2
3
4
5
6
7
8
9
10
11
12
13
14

可以看到主线程中执行thread0.join()代码,会让主线程等待子线程执行完后,主线程再运行。

结论:join方法会让当前线程等待子线程死亡,即主线程等待子线程结束之后才能继续运行。当前线程是指,当前获取到CPU执行权的线程,也就是例子中的主线程。

join方法的JDK源码比较简单,里面就是调用了wait方法。wait方法会让当前线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。

join方法需要注意:虽然join方法里面调用了wait方法,但是源码中并没有地方调用notify/notifyAll方法,所以主线程是怎么被唤醒的呢?

官方文档里面有解释:

This implementation uses a loop of {@code this.wait} calls conditioned on {@code this.isAlive}. As a thread terminates the {@code this.notifyAll} method is invoked.
翻译:

此实现使用以this.isAlive为条件的this.wait调用循环。 当线程终止时,将调用this.notifyAll方法。
因此主线程是在子线程终止时,由子线程调用notifyAll方法,被唤醒的。

2.7 interrupt方法

线程中断相关的方法较多,下面通过官方文档一一介绍:

1、interrupt方法

public void interrupt()
1
官方解释大致如下:

Interrupts this thread.
If this thread is blocked in an invocation of the wait()、join()、sleep()… methods, then its interrupt status will be cleared and it will receive an InterruptedException.
翻译如下:

中断这个线程。
如果这个线程在调用wait(),join(),sleep()…方法时被打断,那么它的中断状态将被清除,它将收到一个InterruptedException
假设main(主线程)、thread0(子线程)都在运行,main线程可以通过执行thread0.interrupt()来中断thread0线程,该方法只是在thread0中设置一个标志,表示它已经被中断,并立即返回。

这里需要注意的是,如果只是单纯的调用interrupt()方法,线程并没有实际被中断,会继续往下执行。

2、interrupted方法

public static boolean interrupted() {
        return currentThread().isInterrupted(true);//参数为true表示:清除线程中断状态
}
1
2
3
官方解释:

Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).
翻译如下:

测试当前线程是否已被中断。此方法清除线程的中断状态。换句话说,如果这个方法被成功调用两次,那么第二次的调用会返回false(除非在方法两次调用中间线程又被中断过)。
如果线程已经被中断,此方法会返回当前线程的中断状态true,而且会调用isInterrupted(true)方法清除线程的中断状态,即让线程的中断状态变为false。在第二次调用的时候就会返回false。

3、isInterrupted方法,有两个重载的方法。

/**
* Tests whether this thread has been interrupted.  The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* 测试这个线程是否被中断。线程的中断状态不会被该方法清除。
*/
public boolean isInterrupted() {
    return isInterrupted(false);//参数为false表示:不清除线程中断状态
}
1
2
3
4
5
6
7
8
9
/**
* Tests if some Thread has been interrupted.  The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*
* 测试某些线程是否已经被中断了。是否重置中断状态需依据传入的参数。
*/
private native boolean isInterrupted(boolean ClearInterrupted);
1
2
3
4
5
6
7
8
下面通过代码理解:

public class ThreadTest {

    public static void main(String[] args) {

        ThreadTest test = new ThreadTest();
        MyThread myThread = test.new MyThread();

        Thread thread0 = new Thread(myThread);

        thread0.start();

        System.out.println(Thread.currentThread().getName() + ":调用interrupt()");
        thread0.interrupt();
        //isInterrupted方法不会清除中断状态
        System.out.println(Thread.currentThread().getName() + ":子线程的中断状态"+ thread0.isInterrupted());

        System.out.println(Thread.currentThread().getName() + ":执行完毕");
    }

    public class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":开始执行");

            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":循环" + "第"+ i + "次");
            }
                        //interrupted方法会清除中断状态
            System.out.println(Thread.currentThread().getName() + ":子线程的中断状态"+ Thread.interrupted());
            System.out.println(Thread.currentThread().getName() + ":子线程的中断状态"+ Thread.interrupted());

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

    }
}
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
运行结果:

main:调用interrupt()
Thread-0:开始执行
main:子线程的中断状态true
Thread-0:循环第0次
main:执行完毕
Thread-0:循环第1次
Thread-0:循环第2次
Thread-0:循环第3次
Thread-0:循环第4次
Thread-0:循环第5次
Thread-0:循环第6次
Thread-0:循环第7次
Thread-0:循环第8次
Thread-0:循环第9次
Thread-0:子线程的中断状态true
Thread-0:子线程的中断状态false
Thread-0:执行结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
从结果中可以看出:

interrupt()不会立刻中断线程,只是设置线程的中断状态为true
isInterrupted()非静态方法,只返回线程的中断状态,不能清除中断状态
interrupted()静态方法,返回当前线程的中断状态,该方法会清除中断状态,第二次调用的时候返回false
---------------------
【转载】
作者:cherry-peng
原文:https://blog.csdn.net/xsp_happyb ... 220?utm_source=copy


3 个回复

倒序浏览
奈斯
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马