美文网首页并发编程
并发编程(二):线程常用的方法

并发编程(二):线程常用的方法

作者: codeMover | 来源:发表于2020-04-03 00:20 被阅读0次

start和run

start方法

  • 功能说明:启动一个新线程,在新的线程运行run方法中的代码
  • start方法只是让线程进入就绪,里面代码不一定立刻运行,每个start方法只能调用一次,多次调用会出现(java.lang.IllegalThreadStateException)异常

start方法

  • 功能说明:新线程启动后会调用的方法
  • 如果在构造Thread对象中传递Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。可以创建Thread的子类对象,来覆盖默认行为。
@Slf4j(topic = "ants.TestStart")
public class TestStart {
    public static void main(String[] args) {
        Thread t = new Thread("t1"){
            @Override
            public void run(){
                log.debug("执行run");
            }
        };
        t.start();
        log.debug("main 执行");
    }
}

sleep和yield

sleep

  • 调用sleep会让当前线程从Running进入Timed Waiting状态
  • 其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
  • 睡眠结束后的线程未必会like得到执行
  • 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
@Slf4j(topic = "ants.TestSleep")
public class TestSleep {
    public static void main(String[] args) {
         //测试线程sleep
        Thread t1 = new Thread("t1") {
            @SneakyThrows
            @Override
            public void run() {
                Thread.sleep(2000);
                log.debug("t1线程停留2秒后执行");
            }
        };
        t1.start();
        log.debug("t1线程状态:{}",t1.getState());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("t1线程状态:{}",t1.getState());
    }
}
@Slf4j(topic = "ants.TestSleepInterrupt")
public class TestSleepInterrupt {
    public static void main(String[] args) throws InterruptedException {
        //睡眠线程调用interrupt,会抛出(InterruptedException)异常
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                //建议使用 TimeUnit.SECONDS.sleep,睡眠时间更直观
                log.debug("enter sleep ...");
                try {
                    TimeUnit.SECONDS.sleep(2);
                    log.debug("t1线程停留2秒后执行");
                } catch (InterruptedException e) {
                    log.debug("sleep interrupt ...");
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        log.debug(" t1 interrupt...");
        t1.interrupt();

    }
}

yield

  • 调用yield会让当前线程从Running进入Runnable状态,然后调度执行其他同优先级的线程。如果这时没有同优先级的线程,name不能保证让当前线程暂停的效果。
  • 具体的实现依赖于操作系统的任务调度器
  • yield代码测试没有那么明显,首先保证两个线程优先级一致,但是t1线程执行yield方法后,虽然从Running进入Runnable状态,但是很有可能再次获得CPU时间片,下面这段代码理想状态下是,线程在执行0的倍数时,暂停当前线程,执行其他线程。比如t1执行到(i=10),t1不在执行下一次打印,让t2打印。
@Slf4j(topic = "ants.TestYield")
public class TestYield {

    public static void main(String[] args)  {
        Test t1 = new Test("t1");
        Test t2 = new Test("t2");
        t1.start();
        t2.start();
    }
}
@Slf4j(topic = "ants.Test")
class Test extends Thread{
    public Test(String name){
        super(name);
    }

    @Override
    public void run() {
        for(int i = 1;i <=100; i++) {
            log.debug("t1线程执行第{}循环",i);
            if(i == 0)
                Thread.yield();
        }
    }
}

sleep与yield对比

  • 让其他线程执行:sleep会明确在睡眠时间让其他线程执行,yield会和runnable状态线程得到执行可能
  • 状态不同:执行sleep后线程状态变为timed waiting,执行yield后线程状态变为runnable
  • 明确的时间:sleep会有明确时间(睡眠时长)不会执行,因为任务调度器不会把CPU时间片分给状态timed waiting;yield可能会得到时间片,立即执行。

线程优先级

  • 线程优先级会提示调度器优先调度该线程,但仅仅是一个提示,调度器可以忽略
  • 如果cpu比较忙,那么优先级高的线程将会获得更多的而时间片,但cpu空闲时,优先级几乎没作用
@Slf4j(topic = "ants.TestPriority")
public class TestPriority {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                int count=0;
                while(true){
                    log.debug("t1线程count值:{}",count++);
                }
            }
        };
        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                int count=0;
                while(true){
                    //yield(); //注释该行,发现打印结果count值接近,打开后t1线程获得CPU时间片多,count值大
                    log.debug("t2线程count值:{}",count++);
                }
            }
        };
        //t1.setPriority(Thread.MAX_PRIORITY);//单核CPU测试线程优先级效果明显
        //t2.setPriority(Thread.MIN_PRIORITY);
        t1.start();
        t2.start();

    }
}

防止CPU只用100%

  1. sleep线程睡眠,适合无锁场景
  2. wait或条件变量控制,都需要加锁,需要相关唤醒操作,一般用户同步场景

join

  • 调用该线程的线程同步,程序才会继续向下执行
  • join可以做限时通过,join(1000)1000毫秒后,如果线程没结束,继续向下执行

interrupt、isInterrupted、interrupt

  • interrupt设置打断标记,正常线程调用方法后,打断状态是true,调用wait、sleep、join线程的打断标记是false
  • isInterrupted 查看线程状态
  • interrupted 查看线程状态,并设置打断标记为false
@Slf4j(topic = "ants.TestInterrupt")
public class TestInterrupt {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            //进入sleep wait 、join线程,异常标记为false
            @Override
            public void run() {
                log.debug("t1 interrupt");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    log.debug("Thread.currentThread().interrupt() before:{}",Thread.currentThread().isInterrupted());
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                    log.debug("Thread.currentThread().interrupt():{}",Thread.currentThread().isInterrupted());
                }
            }
        };
        Thread t2 = new Thread("t2") {
            //正常执行的线程,打断标记为true
            @Override
            public void run() {
                log.debug("t2 interrupt");
                while(true){

                }

            }
        };
        Thread t3 = new Thread("t3") {
            //线程优雅关闭
            @Override
            public void run() {
                boolean flag = true;
                while(flag){
                    if(Thread.currentThread().isInterrupted()){
                        log.debug("t3 interrupt");
                        flag=false;
                    }
                }
                //处理退出循环后续逻辑
                log.debug("t3 退出循环");

            }
        };
        t1.start();
        Thread.sleep(1000);
        t1.interrupt();
        Thread.sleep(1000);
        log.debug("t1线程的打断标记:{}",t1.isInterrupted());
        log.debug("-------------------");
       t2.start();
        Thread.sleep(1000);
        t2.interrupt();
        Thread.sleep(1000);
        log.debug("t2线程的打断标记:{}",t2.isInterrupted());
        log.debug("-------------------");
        t3.start();
        Thread.sleep(1000);
        t3.interrupt();
        Thread.sleep(1000);

        log.debug("t3线程执行结束后打断标记:{}",t3.isInterrupted());
    }
}

park unpark

  • 线程调用LockSupport中的park方法,当前线程会停下来执行,调用线程的interrupt,将打断标记置为false后,继续执行后续代码
@Slf4j(topic = "ants.TestPark")
public class TestPark {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("t1 线程执行中-----");
                LockSupport.park();
                //log.debug("t1打断标记:{}",Thread.currentThread().isInterrupted());//不改变打断标记值
                log.debug("t1打断标记:{}",Thread.interrupted());//将打断标记置为false
                log.debug("t1 park  后面代码-----");
                LockSupport.park();
                log.debug("t1 再次 park  后面代码-----");

            }
        };
        t1.start();
        t1.interrupt();//打断正在执行的park线程
    }
}

相关文章

网友评论

    本文标题:并发编程(二):线程常用的方法

    本文链接:https://www.haomeiwen.com/subject/elhqphtx.html