美文网首页
并发编程专题-01 Java线程

并发编程专题-01 Java线程

作者: 攻城老狮 | 来源:发表于2021-09-12 14:42 被阅读0次

    1.创建和运行线程

    1.1直接使用Thread

    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" run...");
            }
        };
        t.start();
    }
    

    1.2 使用 Runnable 配合 Thread

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run...");
            }
        };
        Thread t = new Thread(r);
        t.start();
    }
    

    原理 使用Thread和Runnable方法之间的关系

    • 方法1是把线程和任务合并在一起,方法2是把线程和任务分开
    • 使用Runnable让任务类脱离Thread的继承体系,更加灵活
    // 查看Thread的源码run()方法,如果target存在,则会调用target的run方法,其中target即为传入的实现Runnable接口的对象,调用该对象的run方法。而若使用Thread,则会重写该方法,执行重写的run方法
    // private Runnable target;
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    

    1.3 FutureTask配合Thread

    1. 实例程序

    FutureTask 可以接收Callable类型的参数,用来处理有返回结果的情况。(前面两种方法无法接收返回结果)

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建任务对象
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName() + " run...");
                return 233;
            }
        });
        //启动线程
        new Thread(task).start();
        //主线程阻塞,同步等待task执行完毕的结果
        Integer result = task.get();
        System.out.println(result);
    }
    
    1. 原理 FutureTask的原理分析
    • FutureTask实现了Runnable接口,重写了run方法。故可以传入至Thread的构造方法中。
    //FutureTask实现了Runnable接口
    public class FutureTask<V> implements RunnableFuture<V>{}
    public interface RunnableFuture<V> extends Runnable, Future<V> {}
    
    //重写了run方法
    public void run() {
        //...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //在FutureTask的run方法中调用实现Callable接口的call方法
                    result = c.call();
                    ran = true;
                } 
                //...
            }
        } finally {
            //...
        }
    
    • 在FutureTask的构造方法中传入实现Callable接口的对象
    // 可以发现Callable接口的call方法有返回值类型,可以返回结果
    @FunctionalInterface
    public interface Callable<V> {
        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        V call() throws Exception;
    }
    

    2.Java线程常用方法

    2.1 start和run方法

    • 直接调用run是在主线程中执行run,没有启动新的线程
    • 使用start是启动新的线程,通过新的线程间接执行run中的代码
    //该程序测试run和start方法的区别
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" run...");
            }
        };
        //t.run();
        t.start();
    }
    

    2.2 sleep方法

    • 调用sleep会让当前线程从Running进入Timed Waiting状态
    • 其他线程可以使用 interrupt方法打断正在睡眠的线程,此时sleep方法会抛出InterruptedException
    • 睡眠结束后的线程未必会立刻得到执行,需要被调度器调度
    • 可以使用 TimeUnit的sleep方法,可读性更好
    //该程序测试sleep的一些特性
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run...");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t.start();
        //用 TimeUnit 的 sleep 代替 Thread 的 sleep
        TimeUnit.SECONDS.sleep(1);
        //进入 Timed Waiting 状态
        System.out.println(t.getState());
        //使用 interrupt 方法打断正在睡眠的线程,
        t.interrupt();
    }
    
    • result
    Thread-0 run...
    TIMED_WAITING
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.yqj.concurrent2.CreateThread$1.run(CreateThread.java:21)
    

    2.3 yield 和 线程优先级

    1. 作用说明

    yield:

    • 调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其他线程
    • 具体的实际情况依赖于操作系统的任务调度器

    线程优先级:

    • 线程优先级会提示调度器优先调度该线程,仅起到提示的作用,调度器可以忽略
    • 如果cpu比较忙,优先级高的线程会获得更多的时间片;但cpu闲的时候,优先级几乎没有作用
    1. 实例程序
    //该程序,测试yield和线程优先级的设置,是否会对线程的执行起到影响
    public static void main(String[] args) {
        Runnable task1 = new Runnable() {
            int count = 0;
    
            @Override
            public void run() {
                while (true) {
                    System.out.println("t1: "+ (++count));
                }
            }
        };
    
        Runnable task2 = new Runnable() {
            int count = 0;
    
            @Override
            public void run() {
                while (true){
                    //                    Thread.yield();
                    System.out.println("    t2: "+ (++count));
                }
            }
        };
    
        Thread t1 = new Thread(task1,"t1");
        Thread t2 = new Thread(task2,"t2");
        //        t1.setPriority(Thread.MIN_PRIORITY);
        //        t2.setPriority(Thread.MAX_PRIORITY);
    
        t1.start();
        t2.start();
    
    }
    

    2.4 join方法

    使用join的目的是需要等待其他线程执行结果返回,再执行本线程

    //该程序的主线程需要等待多个线程的结果返回,同步操作,可以获取到num1和num2修改后的结果,最后的时间测得和最长线程花费的时间一致
    public class TestJoin {
    
        static int num1 = 0;
        static int num2 = 0;
    
        public static void main(String[] args) throws InterruptedException {
            Runnable task1 = new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num1 = 10;
                }
            };
    
            Runnable task2 = new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num2 = 20;
                }
            };
    
            Thread t1 = new Thread(task1, "t1");
            Thread t2 = new Thread(task2, "t2");
    
            long start = System.currentTimeMillis();
            t1.start();
            t2.start();
            //等待另外两个线程返回结果
            t1.join();
            t2.join();
            long end = System.currentTimeMillis();
    
            System.out.println("num1="+num1+" num2="+num2);
            System.out.println("time="+(end-start));
        }
    }
    
    • result
    num1=10 num2=20
    time=2008
    

    2.5 有时效的join方法

    //当到达规定的时间,指定的线程还没有返回结果,则直接向后执行。若在规定时间之内,指定的线程返回了结果,则直接继续即可
    public class TestJoin {
    
    
        public static void main(String[] args) throws InterruptedException {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " run..");
                }
            };
            Thread t = new Thread(task);
            t.start();
            //当到达1秒后,还未返回,则继续执行
            t.join(1000);
            System.out.println("main");
        }
    }
    

    2.6 interrupt方法

    1. 实例程序
    • 使用 interrupt 方法可以打断 sleep、wait 和 join 的线程

    • 打断sleep的线程,会清空打断状态

    • 打断正常运行的线程,不会清空打断状态

    //该程序测试打断sleep和正常运行线程后,打断标记的状态
    public class TestInterrupt {
    
        public static void main(String[] args) throws InterruptedException {
    //        taskSleepInterrupt();
            taskCommonInterrupt();
        }
    
        //测试打断sleep状态的线程
        public static void taskSleepInterrupt() throws InterruptedException {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        //false 清空了打断的标记
                        System.out.println(Thread.currentThread().isInterrupted());
                        e.printStackTrace();
                    }
                }
            };
    
            Thread t = new Thread(task);
            t.start();
    
            TimeUnit.SECONDS.sleep(2);
            t.interrupt();
        }
    
        //测试打断正常运行的线程
        public static void taskCommonInterrupt(){
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    while (true){
                        Thread thread = Thread.currentThread();
                        if (thread.isInterrupted()){
                            //true 不清空标记
                            System.out.println(thread.isInterrupted());
                            System.out.println("interrupt...");
                            break;
                        }
                    }
                }
            };
    
            Thread t = new Thread(task);
            t.start();
    
            t.interrupt();
        }
    }
    
    1. 设计模式 两阶段终止
    //两阶段终止,用于单独的服务器线程在后台持续运行记录日志等信息,当收到关闭线程的消息后,做最后的处理,退出线程
    public class TestInterrupt {
    
        public static void main(String[] args) throws InterruptedException {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    while (true){
                        Thread thread = Thread.currentThread();
                        if (thread.isInterrupted()){
                            System.out.println("do the last things...");
                            break;
                        }
                        try {
                            TimeUnit.MILLISECONDS.sleep(50);
                            System.out.println("run...");
                        } catch (InterruptedException e) {
                            //二阶段终止
                            thread.interrupt();
                            e.printStackTrace();
                        }
                    }
                }
            };
            Thread t = new Thread(task);
            t.start();
            TimeUnit.SECONDS.sleep(1);
            //一阶段终止
            t.interrupt();
       
    }
    
    1. 打断park线程
    //使用interrupt方法可以打断park的线程,此时查看打断状态为true,当打断状态为true后,无法再使用park让线程阻塞,需要清空标志为false才可以正常再park线程
    public class TestInterrupt {
    
        public static void main(String[] args) throws InterruptedException {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    System.out.println("park...");
                    LockSupport.park();//阻塞
                    System.out.println("unpark...");
                    //true
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            };
            Thread t = new Thread(task);
            t.start();
    
            TimeUnit.SECONDS.sleep(1);
            t.interrupt();
        }
    
    }
    

    2.7 守护线程

    默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束

    //该程序当使用setDaemon设置守护线程后,其他线程结束后,该线程也会随之结束,不会再等待剩余的时间
    public class TestDaemon {
    
        public static void main(String[] args) throws InterruptedException {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("run...");
                }
            };
            Thread t = new Thread(task);
            t.setDaemon(true);
            t.start();
    
            TimeUnit.SECONDS.sleep(1);
            System.out.println("main end...");
        }
    }
    

    3.线程的六种状态(基于Java枚举类)

    image-20210609215254437.png
    //六种状态测试
    public class TestState {
    
        public static void main(String[] args) throws InterruptedException {
            //NEW 
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
    
                }
            };
            
            //RUNNABLE
            Thread t2 = new Thread("t2") {
                @Override
                public void run() {
                    while (true){
    
                    }
                }
            };
    
            //TERMINATED
            Thread t3 = new Thread("t3") {
                @Override
                public void run() {
    
                }
            };
    
            //TIMED_WAITING
            Thread t4 = new Thread("t4") {
                @Override
                public void run() {
                    synchronized (TestState.class){
                        try {
                            TimeUnit.SECONDS.sleep(60);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            //WAITING
            Thread t5 = new Thread("t5") {
                @Override
                public void run() {
                    try {
                        t2.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            //BLOCKED
            Thread t6 = new Thread("t6") {
                @Override
                public void run() {
                    synchronized (TestState.class){
                        try {
                            TimeUnit.SECONDS.sleep(60);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            t6.start();
    
            TimeUnit.SECONDS.sleep(1);
            System.out.println(t1.getState());
            System.out.println(t2.getState());
            System.out.println(t3.getState());
            System.out.println(t4.getState());
            System.out.println(t5.getState());
            System.out.println(t6.getState());
        }
    }
    

    4.小结

    • 线程创建的三种方式
    • 线程重要api,如:start,run,sleep,yield,join,interrupt等
    • 线程的六种状态

    相关文章

      网友评论

          本文标题:并发编程专题-01 Java线程

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