Java并发编程(二)——线程中的常见方法(join,yield,sleep等)和线程状态

1、start():启动一个新线程,在新的线程中运行run方法。注意:该方法只是让线程进入就绪状态,里面代码不一定立刻执行(CPU时间片还没分给它)。

2、join():等待线程运行结束,写上t1.join(),程序运行到这句话时会等待线程t1运行结束后再执行后续操作,该方法提供了线程间运行顺序的控制,假设t2线程需要等待t1线程运行完才能执行,则可以在t2线程前加上t1.join()

static int r = 0;
@Test
public void test1() throws InterruptedException {
    log.debug("开始");
    Thread t1 = new Thread(() -> {
        log.debug("开始");
        sleep(1);
        log.debug("结束");
        r = 10;
    },"t1");
    t1.start();
    t1.join();  // 等待t1运行完后,修改了r的值为10,再运行后续程序
    log.debug("结果为:{}", r);
    log.debug("结束");
}

3、join(long n):等待线程运行结束,最多等待n毫秒,称为有时效的等待。
4、setPriority(),getPriority():设置和获取优先级
\quad 优先级从1-10,默认为5。线程优先级会提示调度器优先调度该线程,但其仅仅是一个提示,调度器可以忽略。如果CPU忙,那么优先级高的线程会获得更多时间片,但闲时,优先级失效。

Runnable task1 = () -> {
    int count = 0;
    while(true){
        System.out.println("---->1 " + count);
        count ++ ;
    }
};
Runnable task2 = () -> {
    int count = 0;
    while(true){
        System.out.println("---->2 " + count);
        count ++ ;
    }
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.setPriority(1); t2.setPriority(10);
t1.start(); t2.start();

\quad 通过给t1设置最低的优先级,给t2设置最高的优先级,可以发现打印2的次数远远大于1。
5、getState():获取线程状态

Thread t1 = new Thread("t1") {
    @Override
    public void run() {
        log.debug("running...");
    }
};

System.out.println(t1.getState());  // NEW(新建状态,还没被CPU运行)
t1.start();
System.out.println(t1.getState());  // RUNNABLE(运行状态)

6、interrupt():打断阻塞的线程(可能是由于使用sleep,join,wait导致线程阻塞);也可以打断正在运行的线程。

Thread t1 = new Thread(()->{
    log.debug("sleep");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
log.debug("打断标记: {}", t1.isInterrupted());

\quad 当一个线程被打断,如果没有在该线程内判断是否被打断,该线程还会一直运行下去,因此我们可以在该线程运行逻辑前加入该线程是否被打断的判断,一旦该线程被打断了,就结束该线程。

Thread t1 = new Thread(() -> {
    while(true) {
        boolean interrupted = Thread.currentThread().isInterrupted();
        if(interrupted) {
            log.debug("被打断了, 退出循环");
            break;
        }
    }
}, "t1");
t1.start();

Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();

7、isAlive():线程是否存活
8、sleep():调用sleep会让当前线程从running进行timewaiting状态,注意:Thread.sleep()在哪个线程里被调用就是让哪个线程等待。

Thread t1 = new Thread("t1"){
    @SneakyThrows
    @Override
    public void run() {
        Thread.sleep(2000);
    }
};
t1.start();
// t1刚刚启动,还未到sleep处
log.debug("t1 state: {}", t1.getState()); // t1 state: RUNNABLE
Thread.sleep(1000);
// t1启动后一小段时间,t1进入sleep
log.debug("t1 state: {}", t1.getState()); // t1 state: TIMED_WAITING

\quad 有一种情况是,我们不想线程睡一个固定时间,我希望能在程序中打断线程,可以结合interrupt()方法使用:

Thread t1 = new Thread("t1"){
    @Override
    public void run() {
        log.debug("enter sleep...");
        try{
            Thread.sleep(2000);
        }catch (InterruptedException e){
            log.debug("wake up...");
            e.printStackTrace();
        }
    }
};
t1.start();
Thread.sleep(1000);
log.debug("interrupt...");
t1.interrupt();  // 不要睡眠了

\quad 可以看到,刚开始时t1进入睡眠,经过了1s对t1线程进行打算。

20:40:20.977 c.Test5 [t1] - enter sleep...
20:40:21.976 c.Test5 [main] - interrupt...
20:40:21.976 c.Test5 [t1] - wake up...
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at cn.itcast.test.Test5$4.run(Test5.java:61)

\quad sleep经典应用场景:假设我们的某个线程需要不停的运行,但是又不希望这个线程一直把CPU全部占用,就可以使用sleep,每隔一小段时间运行下就行,如下:

while (true) {
    try {
        System.out.println("该线程一直运行");
        Thread.sleep(100);  // 腾出CPU资源
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

9、yield():让当前线程让出CPU使用权,会使得当前线程从Running进入Runnable就绪状态,然后调度执行其他线程,具体的实现依赖于操作系统的任务调度器,即可能存在无其他线程、让不出去的情况。

Runnable task1 = () -> {
    int count = 0;
    while(true){
        System.out.println("---->1 " + count);
        count ++ ;
    }
};
Runnable task2 = () -> {
    int count = 0;
    while(true){
        Thread.yield();
        System.out.println("---->2 " + count);
        count ++ ;
    }
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.start(); t2.start();

\quad 对线程t2使用yield后,明显CPU给t2时间片少了很多,打印t2的次数只有t1的 1 3 \frac{1}{3} 31
10、不推荐的方法,这些方法已经过时,容易破坏同步代码块,造成线程死锁。

方法名功能说明
stop()停止线程运行
suspend()挂起(暂停)线程运行
resume()恢复线程运行

主线程和守护线程

\quad 默认情况下,java进程需要等待所有线程都运行结束才会结束,有一种特殊的线程叫做守护线程,只要其他非守护线程结束了,即使守护线程未执行完,也会强制结束。例如垃圾回收器就是一种守护线程。使用t1.setDaemon(true)可以将线程t1设置为守护线程,示例如下:

Thread t1 = new Thread(() -> {
    while (true) {
        if (Thread.currentThread().isInterrupted()) {
            break;
        }
    }
    log.debug("结束");
}, "t1");
// 设置为守护线程,当主线程结束后,它也会强制结束,否则该线程是while循环,会一直进行下去。
t1.setDaemon(true);
t1.start();

Thread.sleep(1000);
log.debug("结束");

如何在一个线程t1中优雅终止线程t2,给t2一个料理后事的机会。

错误思路:使用t2线程的stop方法杀死t2。如果t2锁住了共享资源,t2被杀死后再也没有机会释放锁,其他线程将永远无法获取锁。
正确思路:两阶段终止模式

线程状态

从操作系统层面分为5中状态:
在这里插入图片描述

  • 初始状态:在语言层面创建了该线程,还未与操作系统线程关联,例如Thread t1 = new Thread();
  • 可运行状态:也称为就绪状态,调用t1.start()后,CPU时间片还未分到t1线程,该线程等待运行
  • 运行状态:线程获得了CPU时间片,从可运行状态变为运行状态,当CPU时间片用完,又会从运行状态转换至可运行状态,会导致线程的上下文切换。
  • 阻塞状态:加入对一个处于运行状态的线程使用t1.sleep(1000)会让其处于阻塞状态,等sleep时间结束后又会唤醒阻塞的线程,转换至可运行状态。例如如果调用了阻塞API读写文件,此时该线程实际不会用到CPU,会导致上下文切换,进入阻塞状态,等读写操作完成后,会由操作系统唤醒阻塞的现车给,转换到可运行状态。注意:CPU时间片不会分给阻塞状态线程,需要唤醒。
  • 终止状态:线程执行完毕

从java API上分为6种状态
在这里插入图片描述

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called {@code Object.wait()}
         * on an object is waiting for another thread to call
         * {@code Object.notify()} or {@code Object.notifyAll()} on
         * that object. A thread that has called {@code Thread.join()}
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

  • NEW
  • RUNNABLE:运行状态、可运行状态和操作系统层面的阻塞状态都称为RUNNABLE,因此调用t1.start()后线程就进入运行状态
  • BLOCKED:java中三种阻塞状态之一,后续讲完锁后会详解
  • WATING:java中三种阻塞状态之一,后续讲完锁后会详解
  • TIMED_WAITING:java中三种阻塞状态之一,t1.sleep()会让线程进入该状态
  • RERMINATED

下面用个程序演示这6种状态:

public class TestState2 {
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            FileReader.read(Constants.MP4_FULL_PATH);
            while(true){
            }
        }, "t1").start();

        System.out.println("ok");
    }

    @Test
    public void test1() throws IOException {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };

        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                while(true) { // runnable

                }
            }
        };
        t2.start();

        Thread t3 = new Thread("t3") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };
        t3.start();

        Thread t4 = new Thread("t4") {
            @Override
            public void run() {
                synchronized (TestState2.class) {
                    try {
                        Thread.sleep(1000000); // timed_waiting,有等待时限
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t4.start();

        Thread t5 = new Thread("t5") {
            @Override
            public void run() {
                try {
                    t2.join(); // waiting 没有等待时限
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t5.start();

        Thread t6 = new Thread("t6") {
            @Override
            public void run() {
                // t4给对象TestState2上了锁,导致t6拿不到锁,陷入无限等待状态
                synchronized (TestState2.class) { // blocked
                    try {
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t6.start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("t1 state {}", t1.getState());
        log.debug("t2 state {}", t2.getState());
        log.debug("t3 state {}", t3.getState());
        log.debug("t4 state {}", t4.getState());
        log.debug("t5 state {}", t5.getState());
        log.debug("t6 state {}", t6.getState());
        System.in.read();
    }
}
09:54:06.026 c.TestState2 [t3] - running...
09:54:06.527 c.TestState2 [main] - t1 state NEW
09:54:06.528 c.TestState2 [main] - t2 state RUNNABLE
09:54:06.528 c.TestState2 [main] - t3 state TERMINATED
09:54:06.528 c.TestState2 [main] - t4 state TIMED_WAITING
09:54:06.528 c.TestState2 [main] - t5 state WAITING
09:54:06.528 c.TestState2 [main] - t6 state BLOCKED
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值