从零开始的并发世界生活-第三天

第三天主要学习线程中的常见方法~

1.常见方法概述

方法名功能说明注意
start()启动一个新线程,在新的线程运行run方法中的代码start方法只是让线程进入就绪,里面代码不一定立刻运行(CPU的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
run()新线程启动后就会调用run方法如果在构造Thread的时候传入了Runnable参数,则线程启动后会调用Runnble中的run方法,否则默认不执行任何操作,但可以创建Thread的子类对象,来覆盖默认行为(就是重写run方法)
join()等待线程运行结束用于线程间的通信
join(long n)等待线程运行结束,但是最多等待n毫秒
getId()获取线程长整型的idid唯一
getName()获取线程名
setName(String)设置线程名
getPriority()获取线程优先级
setPriority(String)修改线程优先级java中规定线程优先级是1~10的整数,较大优先级能提高给线程被CPU调度的几率,但是在java中这么设置效果不是很明显
getState()获取线程状态Java中线程状态是用6个enum表示,分别为:NEW、RUNNABLE、BLOCKED、WAITING、TIMED、TERMINATED
isInterrupted()判断是否被打断不会清除打断标记
isAlive()判断线程是否还存活(即线程还有没有运行完毕)不会清除打断标记
interrupt()打断线程如果被打断线程正在sleep,wait,join会导致被打断的线抛出nterruptedException,并清除打断标记;如果打断的是正在运行的线程,则会设置打断标记;park的线程被打断,也会设置打断标记
interrupted()(static)判断当前线程是否被打断会清除打断标记
currentThread()(static)获取当前正在执行的线程
sleep(long n)(static)让当前执行的线程休眠n毫秒,休眠期间让出cpu时间片给其他线程
yield()(static)提示线程调度器让出当前线程对CPU的使用主要是为了测试和调试

2.start & run

start方法最后还是会调用run方法,那我们直接调用run方法不可以吗?
不可以,因为start方法在调用run方法之前,是先开启了一个新线程,然后新开启的线程再调用run方法执行任务,而直接调用run方法就是纯粹的方法调用,是由主线程进行的。
案例:

@Slf4j(topic="c.Test7")
public class Test7 {
   public static void main(String[] args) {
      Thread t1 = new Thread("t1"){
         @Override
         public void run() {
            log.debug("running");
         }
      };
      // run只是对方法的调用,还是在主线程中调用的run,而start则是开启线程并由开启的新线程调用run方法
      t1.run();
      t1.start();
   }
}

执行结果:

17:25:10.780 c.Test7 [main] - running
17:25:10.787 c.Test7 [t1] - running

可见直接调用run方法是由主线程调用的run方法

3.getState

直接看案例

@Slf4j(topic="c.Test8")
public class Test8 {
    public static void main(String[] args) {
        //演示线程状态
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                log.debug("running");
            }
        };

        System.out.println(t1.getState());
        t1.start();
        System.out.println(t1.getState());
        t1.start();
    }
}

执行结果:

NEW
RUNNABLE
17:26:50.561 c.Test8 [t1] - running
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:710)
	at com.example.juc.test.Test8.main(Test8.java:23)

线程状态为runnable的时候再进行start,则会抛出IllegalThreadStateException异常

4.sleep & yield

sleep和yield都可以使线程进行休眠。

(1)sleep

  • sleep可以使线程从Running状态进入Timed Wating状态(TIMED_WAITING)
    案例:
@Slf4j(topic="c.Test9")
public class Test9 {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("running");
            }
        };
        t1.start();
        log.debug("t1.state:{}",t1.getState());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("t1.state:{}",t1.getState());
    }
}

主线程在启动t1线程后就进入休眠,以确保主线程苏醒后t1是休眠状态来获取t1的状态。
执行结果:

17:49:27.253 c.Test9 [main] - t1.state:RUNNABLE
17:39:13.491 c.Test9 [main] - t1.state:TIMED_WAITING
17:39:13.988 c.Test9 [t1] - running
  • 其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
    案例:
@Slf4j(topic="c.Test10")
public class Test10 {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    log.debug("t1线程进入睡眠...");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.debug("wake up!");
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        try {
            log.debug("主线程进入睡眠...");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("主线程即将中断t1的睡眠...");
        t1.interrupt();
    }
}

执行结果:

17:59:57.693 c.Test10 [main] - 主线程进入睡眠...
17:59:57.698 c.Test10 [t1] - t1线程进入睡眠...
17:59:58.204 c.Test10 [main] - 主线程即将中断t1的睡眠...
17:59:58.205 c.Test10 [t1] - wake up!
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.example.juc.test.Test10$1.run(Test10.java:17)
  • 睡眠结束后的线程未必会立刻得到执行,需要等到任务调度器将时间片分给此线程时才能继续执行
  • 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性,
Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1); //睡眠一秒
                    TimeUnit.MINUTES.sleep(1); //睡眠一分钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

在这里插入图片描述

  • sleep的应用
    在编程中如果需要服务器的话,我们要确保服务器一直在运行,所以会让服务器一直执行一个while(true)循环,而为了防止服务器只执行一个while(true)空转,一般都会写为:
    while (true){
    Thread.sleep(50);
    }
    
    来防止服务器空转浪费cpu。
    • 可以用wait或条件变量达到类以的效果
    • 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
    • sleep适用于无需锁同步的场景

(2)yield

  • 调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其他线程,yield的本意即“让出”

  • 具体的实现还是要依赖操作系统的任务调度器,可能“让也让不出去”

  • 和sleep的区别:

    1. sleep是让线程从Running进入Time Wating状态,yield是让线程从Running进入Runnable状态,一个线程是可能再从Runnable状态变成Running状态的,而Time Watiing阻塞状态不能直接转变成Running状态:
      在这里插入图片描述
    2. sleep会有一个等待时间,而yield没有等待时间,直接让出
  • 线程优先级:
    1.setPriority方法:设置优先级,1-10,越大优先级越高,默认为5
    2.线程优先级会提示(hint)调度器优先调度该线程但它仅仅是一个提示,调度器可以忽略它
    3.如果cpu比较忙,那么优先级高的线程会获得更多的时间片,但cpu闲时,优先级几乎没有作用,所以yield跟线程优先级不是很容易能控制任务的调度

5.join方法

join方法的作用就是等到某个线程执行结束,让线程进入TIMED_WAITING状态来等待某个线程执行结束。
join方法的应用之一就是实现同步;
首先回顾同步的概念:

  • 需要等待结果返回才能继续运行就是同步
  • 不需要等待结果返回就能继续运行就是异步

案例:

@Slf4j(topic="c.Test12")
public class Test12 {
   public static void main(String[] args) throws InterruptedException {
         test();
   }

   public static void test() throws InterruptedException {
      Thread t1 = new Thread("t1") {
         @Override
         public void run() {
            try {
               TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            log.debug("t1 running");
         }
      };
      Thread t2 = new Thread("t2") {
         @Override
         public void run() {
            try {
               TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            log.debug("t2 running");
         }
      };
      t1.start();
      t2.start();
      log.debug("开始执行...");
      long startTime = System.currentTimeMillis();
      log.debug("t1_join...");
      t1.join();
      log.debug("t2_join...");
      t2.join();
      long endTime = System.currentTimeMillis();
      log.debug("执行时间为:{}",endTime - startTime);
   }
}

执行结果:

20:07:45.393 c.Test12 [main] - 开始执行...
20:07:45.395 c.Test12 [main] - t1_join...
20:07:46.395 c.Test12 [t1] - t1 running
20:07:47.401 c.Test12 [t2] - t2 running
20:07:47.401 c.Test12 [main] - t2_join...
20:07:47.401 c.Test12 [main] - 执行时间为:2006

在主线程中执行t1.join(),只会使主线程等待t1执行结束,t2不受影响继续执行,所以最后整个执行时间就是2秒
图例:
在这里插入图片描述

  • join的限时同步:join(long mills),当线程执行时长超过mills时,就不会继续等待线程执行结束,直接执行之后的代码。

6.interrupt方法

interrupt方法可以“”打断“sleep、wait、join这些让线程处于阻塞的线程,也可以打断运行状态的线程,线程被打断后会抛出 InterruptedException,注意这里的打断并不是让这些线程强制改变运行状态,而是只改变打断标记
打断标记:打断标记是一个boolean值。当线程被打断后,打断标记会变为true,但是打断sleep、wait、join的线程,会清空打断标记,也就是重置为false。
isInterrupted方法:查看打断方法。
案例:

@Slf4j(topic="c.Test14")
public class Test14 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt...");
        t1.interrupt();
        Thread.sleep(1000);
        log.debug(String.valueOf(t1.isInterrupted()));
    }
}

执行结果:

20:26:10.150 c.Test14 [main] - interrupt...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.example.juc.test.Test14$1.run(Test14.java:16)
20:26:10.152 c.Test14 [main] - true

提问:为什么在t1.interrupt()后还要让主线程再睡一会儿?
因为如果此时马上打印输出t1的打断标记,此时很可能还是输出的true而不是false,那是因为这个时间段内打断标记还没来得及被清除,所以先让主线程睡一会儿再打印就能让打断标记来得及被清除了。

那么interrupt有什么作用呢?
首先我们需要知道,interrupt只是改变打断标记,要想通过interrupt实现对线程运行状态的控制,则需要我们自定义逻辑规则。
案例:

@Slf4j(topic="c.Test15")
public class Test15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                int i = 0;
                while (true){
                    try {
                        Thread.sleep(5000);
                        i++;
                        log.debug("{}",i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        t1.start();
        Thread.sleep(1000);
        t1.interrupt();
    }
}

执行结果:

20:43:19.740 c.Test15 [t1] - 1
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.example.juc.test.Test15$1.run(Test15.java:18)
20:43:20.747 c.Test15 [t1] - 2
20:43:21.259 c.Test15 [t1] - 3
20:43:21.772 c.Test15 [t1] - 4
20:43:21.259 c.Test15 [t1] - 5
20:43:21.772 c.Test15 [t1] - 6
.........
.........
.........
.........
.........
.........

上述案例只是对线程状态进行了改变,没有对线程状态进行判断,所以会一直执行下去。
修改后的案例:

@Slf4j(topic="c.Test15")
public class Test15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                int i = 0;
                while (true){
                    if (Thread.currentThread().isInterrupted()){
                        log.debug("跳出循环");
                        break;
                    }
                        i++;
                        log.debug("{}",i);
                }
            }
        };

        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt...");
        t1.interrupt();
        log.debug(String.valueOf(t1.isInterrupted()));


    }
}

执行结果:
....
....
....
20:50:22.506 c.Test15 [t1] - 273376
20:50:22.506 c.Test15 [t1] - 273377
20:50:22.506 c.Test15 [t1] - 273378
20:50:22.506 c.Test15 [main] - interrupt...
20:50:22.506 c.Test15 [t1] - 273379
20:50:22.507 c.Test15 [main] - true
20:50:22.507 c.Test15 [t1] - 跳出循环

需要将t1线程内的sleep去掉,否则打断sleep线程不会改变打断标记。
上面的案例可以通过控制对打断标记的判断来控制线程的运行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值