Thread API的详细介绍

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.toList;

public class Test {
    public static void main(String[] args){
//        ThradSleep.test();
//        ThreadPriority.test();
//        ThreadPriority.test();
//        ThreadId.test();
//        CurrentThread.test();
//        ThreadInterrupt.test();
//        ThreadIsInterrupted.test();
//        ThreadIsInterrupted2.test();
//        ThreadInterrupted.test();
//        ThreadTest.test();
//        ThreadJoin.test();
//        FightQueryExample.test();
        FlagThreadExit.test();`
    }
}

/*
    3.1.1 sleep方法介绍

        public static void sleep(long millis) throws InterruptedException
        public static void sleep(long millis, int nanos) throws InterruptedException

    3.1.2 使用TimeUnit代替Thread.sleep

        TimeUnit可以省去换算的步骤,比如线程想休眠3小时24分17秒88毫秒:
            TimeUnit.HOURS.sleep(3);
            TimeUnit.MINUTES.sleep(3);
            TimeUnit.SECONDS.sleep(3);
            TimeUnit.MILLISECONDS.sleep(3);

            ——我是真的需要把Java枚举部分看一下了。。。
 */
class ThradSleep{
    public static void test(){
        new Thread(()->{
            long startTime=System.currentTimeMillis();
            sleep(2_000L);
            long endTime=System.currentTimeMillis();
            System.out.println("Total: "+(endTime-startTime));
        }).start();
        long startTime=System.currentTimeMillis();
        sleep(3_000L);
        long endTime=System.currentTimeMillis();
        System.out.println("Main Total: "+(endTime-startTime));
    }

    private static void sleep(long ms){
        try{
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
    3.2.1 yield方法介绍

    调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态,这个方法一般不常用

        ——Java8的流和Lam,早看早练习,稳赚不赔!!!


    3.2.2 yield和sleep

    其本质区别:
        1.sleep会导致当前线程暂停指定的之间,没有CPU时间片的消耗。(这个CPU时间片消耗指的是什么?)
        2.yield只是对CPU调度器的一个暗示,可能导致线程上下文的切换。

        3.sleep会使线程短暂block,会在指定的时间内释放CPU资源
        4.yield会使RUNNING状态的Thread进入RUNABLE状态

        5.sleep几乎会百分百地完成指定时间的休眠,而yield的提示并不一定有用

        6.一个线程内调用了sleep,另一个线程调用了interrupt,这个调用了sleep的线程会捕获到
        中断信号,而yield不会。
 */
class ThreadYield{
    public static void test() {
        IntStream.range(0,2).mapToObj(ThreadYield::create)
                .forEach(Thread::start);
    }

    private static Thread create(int index) {
        return new Thread(()->{
            if (index == 0) {
                Thread.yield();
            }
            System.out.println(index);
        });
    }
}

/*
    3.3 设置线程优先级

        public final void setPriority(int newPriority); :为线程设定优先级
        public final int getPriority();                 :获得线程的优先级

    3.3.2 线程优先级源码分析

    要点:
        1.线程的优先级不能小于1,不能大于10;
        2.如果优先级大于group的优先级,则优先级会被设置为group的优先级;
 */
class ThreadPriority{
    public static void test(){
        /* 实验设置优先级
        Thread t1 = new Thread(()->{
            while(true){
                System.out.println("t1");
            }
        });

        Thread t2 = new Thread(()->{
            while(true){
                System.out.println("t1");
            }
        });

        t1.setPriority(3);
        t2.setPriority(10);

        t1.start();
        t2.start();
        */

        /* 实验线程优先级和组优先级关系
        ThreadGroup group = new ThreadGroup("test");
        group.setMaxPriority(7);

        Thread t = new Thread(group,"test-thread");
        t.setPriority(10);
        System.out.println("t.getPriority():"+t.getPriority());
        */

        /* 实验线程默认优先级 */
        Thread t1 = new Thread();
        System.out.println("t1 priority:"+t1.getPriority());

        Thread t2 = new Thread(()->{
            Thread t3 = new Thread();
            System.out.println("t3 priority:"+t3.getPriority());
        });
        t2.setPriority(6);


        t2.start();
        System.out.println("t2 priority:"+t2.getPriority());
    }
}

/*
    3.4 获取线程ID

        public long getId();
 */
class ThreadId{
    public static void test(){
        System.out.println("main id: "+Thread.currentThread().getId());

        Thread t = new Thread("test");

        System.out.println("t id: "+t.getId());

    }
}

/*
    3.5 获取当前线程

        public static Thread currentThread();
 */
class CurrentThread{
    public static void test(){
        Thread t = new Thread() {
            public void run() {
                System.out.println(Thread.currentThread() == this);
            }
        };
        t.start();

        String name = Thread.currentThread().getName();
        System.out.println("main".equals(name));
    }
}

/*
    3.6 设置线程上下文类加载器

        public ClassLoader getContextClassLoader()        :获取
        public void setContextClassLoader(ClassLoader cl) :设置
 */

/*
 *************************************************************************
 *************************************************************************
 *************************************************************************
 *************************************************************************
 */

/*
    3.7 线程interrupt

        public void interrupt()
        public static boolean interrupted()
        public boolean isInterrupted()

        ——标记一下,我对interrupted()方法有点疑惑,这个方法是怎么办到把我一个
            对象的标识位给复原了的。额,很尴尬,它调用了一个currentThread方法,
            然后间接的设置了这个标志位,也就说明这个方法只能在某个Thread内部调用。

    3.7.1 interrupt

    如下方法的调用会使得当前线程进入阻塞状态:
        1.Object的wait(),wait(long),wait(long,int)
        2.Thread的sleep(long),sleep(long,int)
        3.Thread的join,join(long),join(long,int)
        4.InterruptibleChannel的io操作(这个很陌生!!!)
        5.Selector的wakeup方法(这个很陌生!!!)
        6.其他方法

    如果另外一个线程调用阻塞线程的interrupt方法,则会打断这种阻塞,因此这种方法有时
    会被称为可中断方法。打断一个线程并不等于该线程的生命周期的结束,仅仅是打断了当前
    线程的阻塞状态。一旦线程在阻塞情况下被打断了,都会抛出一个称为InterruptedException
    的异常,这个异常就像一个signal一样,高诉这个刚刚被中断的线程,哦,我刚刚是因为
    中断被唤醒的啊~~~


    要点:
        interrupt()方法到底做了什么?在一个线程内部存在一个名为interrupt flag的标识
        如果一个线程被interrupt,那么它的flag将会被设置。如果当前线程只在执行可中断的
        方法,且被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除~~~(什么乱
        七八糟啊啊啊啊)
 */
class ThreadInterrupt{
    public static void test(){
        Thread t = new Thread(()->{
            try{
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("Oh,i am be interrupted...");
            }
        });
        t.start();
        try {
            TimeUnit.MILLISECONDS.sleep(2);
            t.interrupt(); //去叫醒这个兄弟!!!
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
    3.7.2 isInterrupted

    isInterrupted是Thread的一个成员方法,它主要判断当前线程是否被中断,该方法仅仅是对
    interrupt标识的一个判断,并不会影响标识发生任何改变。
        ——我感觉作者没有把阻塞和中断的许多细节给讲清楚
 */
class ThreadIsInterrupted{
    public static void test(){
        Thread t = new Thread(){
            @Override
            public void run() {
                while(true){
                    //这个地方真的能被中断么???
                    //do nothing
                }
            }
        };

        t.start();
        try {
            TimeUnit.MILLISECONDS.sleep(2);
            System.out.printf("Thread is interrupted? %s",t.isInterrupted());
            t.interrupt();
            System.out.printf("Thread is interrupted? %s",t.isInterrupted());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadIsInterrupted2{
    public static void test(){
        Thread t = new Thread(){
            @Override
            public void run() {
                while(true){
                    try{
                        TimeUnit.MINUTES.sleep(1);
                    } catch (InterruptedException e) {
                        System.out.printf("I am be interrupted? %s\n",isInterrupted());
                    }
                }
            }
        };

        t.start();
        try {
            TimeUnit.MILLISECONDS.sleep(2);
            System.out.printf("Thread is interrupted? %s \n",t.isInterrupted());
            t.interrupt();
            System.out.printf("Thread is interrupted? %s \n",t.isInterrupted());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
===========================================================================
                            实验总结
===========================================================================
实验结果分析:
    1.调用interrupt这个方法了,这个方法只是简简单单的设置了一个flag
    2.调用了isInterrupted这个方法了,也只是简简单单的去访问一下这个flag
    3.线程中如果没有正在阻塞的方法, 你调用了interrupt是没有任何意义的,你设置的
        这个flag根本没有方法去访问
    4.如果线程正在某个方法上阻塞这,那么这个线程被分到时间片时,肯定先检查一下
        这个flag的值,如果这个flag的值代表中断发生了,就让这个阻塞的方法返回
        ,同时将这个flag复原。
===========================================================================
 */

/*
    3.7.3 interrupted

    interrupted是一个静态方法,调用这个方法会擦除线程的interrupt表示,需要注意
    的是,如果线程被打断了,那么第一次调用interrupted方法会返回true,并且立即擦
    除interrupt标识~~~
        ——我想知道的是,这个东西有什么用!!!
 */
class ThreadInterrupted{
    public static void test() {
        Thread t = new Thread(){
            @Override
            public void run() {
                while (true) {
                    /*
                        我有点不懂这个地方???,你调用的是一个
                        静态方法,来消除的是一个对象的标识。。。
                     */
                    System.out.println(Thread.interrupted());
                }
            }
        };
        t.setDaemon(true);
        t.start();

        try {
            TimeUnit.MILLISECONDS.sleep(2);
            t.interrupted();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
    3.7.4 interrupt注意事项

        如果一个线程设置了interrupt标识,那么接下来的可中断方法会立即中断~~~
            ——不然会造成中断信号丢失,问题更大
 */
class ThreadTest{
    public static void test() {
        System.out.println("Main thread is interrupted:"+Thread.interrupted());
        Thread.currentThread().interrupt();
        System.out.println("Main thread is interrupted now?"+Thread.currentThread().isInterrupted());

        try{
            TimeUnit.MINUTES.sleep(1);
        } catch (InterruptedException e) {
            System.out.println("I will be interrupted still.");
        }

    }
}

/*
    3.8 线程join

        public final void join() throws InterruptException
        public final synchronized void join(long millis, int nanos) throws InterruptException
        public final synchronized void join(long millis) throws InterruptException

    3.8.1 线程join方法详解

    有两种调用join的方法,main线程创建了A线程对象,然后调用A的join方法,这时候main线程会
    等待A线程完成,然后在执行。还有一种就是在A线程内部的run方法中,调用了自生的join方法,不
    知道在这种情况下会发生什么事情。
 */
class ThreadJoin{
    public static void test() {
        List<Thread> threads = IntStream.range(1,3)
                .mapToObj(ThreadJoin::create)
                .collect(toList());

        threads.forEach(Thread::start);

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"#"+i);
            shortSleep();
        }
    }

    private static Thread create(int seq) {
        return new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"#"+i);
            }
        },String.valueOf(seq));
    }

    private static void shortSleep() {
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
    3.8.2 join方法结合实战

    需求:
        如果你有一个APP,主要用于查询航班信息,你的APP是没有这些实时数据的,当用户发起查询
        请求时,你需要到各大航空公司的接口获取信息,最后同意整理加工返回到APP客户端。当然
        JDK自带了很多高级工具,比如CountDownLatch和CyclieBarrier等都可以完成类似的功能,

        该例子是典型的串行任务局部并行化处理~~~用户在APP客户端输入出发地北京和目的地上海后,
        服务端接受到这个请求之后,先来验证用户的信息,然后到各大航空公司的接口查询信息,最后
        经过整理加工返回给客户端,每一个航空公司的接口不会都一样,获取数据格式也不一样,查询
        的速度也存在差异,如果再更航空公司进行串行化交互(逐个地查询),很明显客户端需要等待
        很长的时间,这样的话,用户体验就会非常差。如果我们将每个航空公司的查询都交给一个线程
        去工作,然后在它们结束之后同意对数据进行整理,这样就可以极大的节约时间,从而提高用户
        体验效果。
 */
interface FightQuery{
    List<String> get();
}

class FightQueryTask extends Thread implements FightQuery{

    private final String origin;
    private final String destination;
    private final List<String> fightList = new ArrayList<>();

    public FightQueryTask(String airline,String origin,String destination){
        super("["+airline+"]");
        this.origin=origin;
        this.destination=destination;
    }

    @Override
    public void run() {
        System.out.printf("%s-query from %s to %s \n",getName(),origin,destination);

        int randomVal = ThreadLocalRandom.current().nextInt(10);
        try{
            TimeUnit.SECONDS.sleep(randomVal);
            this.fightList.add(getName()+"-"+randomVal);
            System.out.printf("The Fight:%s list query successful\n",getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<String> get() {
        return this.fightList;
    }
}

class FightQueryExample{
    private static List<String> fightCompany = Arrays.asList(
            "CSA","CEA","HNA"
    );

    public static void test() {
        List<String> results = search("SH","BJ");
        System.out.println("=================result=================");
        results.forEach(System.out::println);
    }

    private static List<String> search(String original,String dest){
        final List<String> result = new ArrayList<>();

        List<FightQueryTask> tasks = fightCompany.stream()
                .map(f->createSearchTask(f,original,dest))
                .collect(toList());

        tasks.forEach(Thread::start);
        tasks.forEach(t->{
            try{
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        tasks.stream().map(FightQueryTask::get)
                .forEach(result::addAll);

        return result;
    }

    private static FightQueryTask createSearchTask(
            String fight,
            String original,
            String dest
    ){
        return new FightQueryTask(fight,original,dest);
    }
}

/*
 *************************************************************************
 *************************************************************************
 *************************************************************************
 *************************************************************************
 */

/*
    3.9 如何关闭一个线程

    3.9.1 正常关闭
        1.线程结束,生命周期正常结束
        2.捕获中断信号关闭线程
            用过new Thread的方法创建线程,因此在用这种方式创建的一个线程中往往会循环地执行
            某个任务,比如心跳检查,不断地接受网络消息报文等,系统决定退出的时候,可以借助中断
            线程的方式使其退出。
                ——因为用new Thread创建线程成本高,所以我们不能把它当快餐线程,所以我们
                    得让它疯狂的循环处理事务,又因为它在疯狂的循环,所以我们得用中断的方式
                    让它退出来。。。阿门
 */
class InterrupThreadExit{
    public static void test() {
        /*
            没有调用可中断方法的,可以利用检测标志位。。。
         */
        Thread t = new Thread(){
            public void run(){
                System.out.println("I will start work");
                while(!isInterrupted()){
                    //working
                }
                System.out.println("I will be exiting...");
            }
        };

        /*
            调用了可中断方法的,可以通过捕获中断异常。。。
         */
        Thread t2 = new Thread(){
            public void run(){
                System.out.println("I will start work");

                while(true){
                    try{
                        TimeUnit.MILLISECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        break;
                    }
                }

                System.out.println("I will be exiting...");
            }
        };

        t.start();
        try {
            TimeUnit.MINUTES.sleep(1);
            System.out.println("System will be shutdown.");
            t.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/*
        3.使用volatile开关控制
            由于interrupt标识很有可能白擦除,或者逻辑单元中不会调用任何可中断方法,所以使用
            volatile修饰的开关flag关闭线程也是一种常用的做法:
 */

class FlagThreadExit{
    static class MyTask extends Thread{
        private volatile boolean close = false;

        @Override
        public void run() {
            System.out.println("I will start work");
            while(close&&!isInterrupted()){
                //working
            }
            System.out.println("I will be exiting.");
        }

        public void close(){
            this.close=true;
            this.interrupt();
        }
    }

    public static void test() {
        MyTask t = new MyTask();
        t.start();
        try {
            TimeUnit.MINUTES.sleep(1);
            System.out.println("sys will be shutdown.");
            t.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

/*
    3.9.2 异常退出

    在一个线程的执行单元中,是不允许抛出checked异常的。如果线程在运行过程中需要捕获
    checked异常并且判断是否还有运行下去的必要,那么此时可以将checked异常封装封装
    成unchecked异常抛出,进而结束线程的生命周期。

    3.9.3 进程假死

    进程出现假死,可能是某个线程阻塞了,或者线程出现了死锁的情况。可以利用jstack、
    jconsole、jvisualvm等工具进行诊断。
 */

 

——《Java高并发编程详解》

转载于:https://www.cnblogs.com/junjie2019/p/10585451.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值