美文网首页
五、Java高级特性(多线程基础使用篇)

五、Java高级特性(多线程基础使用篇)

作者: 大虾啊啊啊 | 来源:发表于2021-05-28 09:19 被阅读0次

    一、使用多线程有什么优点?

    使用多线程可以最大限度的利用cpu的空闲时间来处理其他任务,比如一边让操作系统处理正在打印的数据,一边使用Word编辑文档。CPU在这些任务之间不停的切换,由于切换的速度非常快,给使用者的感受就是这些任务几乎是同时执行的。


    image.png

    以上图1-3我们发现在单任务也就是单线程的情况下,任务2必须要等待任务1执行完才能执行。而在1-4中我们使用多线程的情况下,任务2不需要等待任务1执行完毕,CPU可以在任务1和2之间来回的切换。

    二、Java中使用多线程

    在Java的JDK开发包中已经自带了对多线程技术的支持。实现多线程编程的方式主要有两种,一种是继承Thread,一种是实现Runnable接口。

    1、继承Thread

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            InnerThread t = new InnerThread();
            t.start();
        }
    }
    
    class InnerThread extends Thread {
        @Override
        public void run() {
            super.run();
            System.out.println("使用继承Thread启动了一个线程");
        }
    }
    
    
    使用继承Thread启动了一个线程
    

    我们创建了一个InnerThread 类,继承了Thread,并重写了run方法。在main函数中我们创建InnerThread 对象,并调用了start方法,这样就启动了一个线程。线程的工作就是在run方法中。

    2、实现Runnable接口

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
        }
    }
    
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("使用继实现Runnable启动了一个线程");
        }
    }
    
    使用继实现Runnable启动了一个线程
    

    以上我们使用实现Runnable接口也启动了一个线程。线程具体的工作在Runnable接口的run方法中。我们可以看到Thread类其实也实现了Runnable接口

    public
    class Thread implements Runnable {
        /* Make sure registerNatives is the first thing <clinit> does. */
        private static native void registerNatives();
        static {
            registerNatives();
        }
    

    我们也可以通过传入Thread对象的方式启动一个线程,其实和实现Runnable是一样的。

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            Thread t = new Thread(new MyThread());
            t.start();
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            System.out.println("通过传入Thread对象的方式启动一个线程");
        }
    }
    
    
    通过传入Thread对象的方式启动一个线程
    

    三、Thread的一些常用Api

    1、Thread.currentThread()

    currentThread方法,指的是获取当前代码块运行所在的线程。

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
            System.out.println("1、当前线程:"+Thread.currentThread().getName());
    
        }
    }
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("2、当前线程:"+Thread.currentThread().getName());
        }
    }
    
    1、当前线程:main
    2、当前线程:Thread-0
    

    我们可以看到main方法运行所在的线程的名字为main,而MyRunnable中的run方法运行所在的线程名字为Thread-0

    2、isAlive()方法

    判断当前线程是否处于活动的状态。活动状态指的是线程已经启动尚未终止,即线程处于正在运行或者准备开始运行的状态。

    public class Main {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
            System.out.println("1当前线程状态:"+t.isAlive());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("2当前线程状态:"+t.isAlive());
        }
    }
    class MyRunnable implements Runnable {
        @Override
        public void run() {
    
        }
    }
    
    1当前线程状态:true
    2当前线程状态:false
    
    

    在代码中我们可以理解成调用start方法后线程变为活动的状态。以上我们看到调用start方法只会,由于我们启动的线程没有马上执行完毕,我们打印了isAlive结果是true,当我们在main方法中休眠了1000毫秒,之后再去打印isAlive,我们启动的线程已经执行完毕,所以结果是false。在上面的例子我们我们其实有两个线程,一个是main方法所在的main线程,一个是我们启动的线程,我们可以叫他做A线程。我们在main函数所在的线程中启动了一个A线程,这两个线程是异步执行任务,不需要排队执行。启动A线程之后,我们休眠了1000毫秒,我们休眠的线程是Main线程,对A线程没有影响,当我们再去打印A线程的isAlive方法的时候,结果是false,因为过了1000毫秒,A线程已经执行完毕,处于非活动状态。

    3、sleep

    让当前线程休眠(暂停执行)一段时间。

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
            System.out.println("我们在"+Thread.currentThread().getName()+"休眠了1000毫秒");
    
        }
    }
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我们在"+Thread.currentThread().getName()+"休眠了1000毫秒");
        }
    }
    
    我们在main休眠了1000毫秒
    我们在Thread-0休眠了1000毫秒
    
    

    4、interrupt方法

    使用interrupt方法来终止线程。

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            MyThread t = new MyThread();
            t.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t.interrupt();
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 500000; i++) {
                if (isInterrupted()) {
                    break;
                }
                System.out.println("我正在执行..." + i);
            }
            System.out.println("执行完毕");
    
        }
    }
    
    
    
    
    
    我正在执行...
    我正在执行...
    我正在执行...
    我正在执行...
    我正在执行...
    我正在执行...
    我正在执行...
    我正在执行...
    true
    执行完毕
    

    在上面的例子中,我们在线程的run方法中,写了一个for循环,通过isInterrupted的返回值来判断是否跳出循环,结束循环里的工作。在Main方法中,我们启动了一个线程,休眠1000毫秒之后,我们调用interrupt方法,中断了线程。所以看到以上的打印结果是执行了一段时间for循环里的代码之后,跳出循环并打印了执行完毕。以上的方式我们可以结束掉for循环里的工作。但是线程没有真正被执行完毕,因为后面还是打印了执行完毕的字样,说明线程还是在工作。那我们怎么样才能真正的停止线程呢?

    package com.company;
    
    public class Main {
        public static void main(String[] args) {
            MyThread t = new MyThread();
            t.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t.interrupt();
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 500000; i++) {
                    if (isInterrupted()) {
                        throw new InterruptedException();
                    }
                    System.out.println("我正在执行..." + i);
                }
                System.out.println("执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
    
        }
    }
    
    
    我正在执行...177357
    我正在执行...177358
    我正在执行...177359
    我正在执行...177360
    我正在执行...177361
    我正在执行...177362
    我正在执行...177363
    我正在执行...177364
    我正在执行...177365
    我正在执行...177366
    我正在执行...177367
    我正在执行...177368
    java.lang.InterruptedException
        at com.company.MyThread.run(Main.java:22)
    

    我们通过抛出InterruptedException异常的方式来停止线程,我没看到执行完毕这几个字样没有执行了。

    5、yield

    yield方法指的是放弃当前CPU资源,给其他线程去占用CPU资源。但是放弃时间不确定,有可能刚放弃又马上获得CPU时间片。因为CPU调度线程是随机的。

    6、线程的优先级

    通过setPriority方法可以设置线程的优先级,我们知道CPU调度线程是随机的,设置线程的优先级越高,可以使得线程获得CPU的资源越多。也就是CPU优先执行优先级高的线程。

    优先级具有继承性

    A线程启动B线程,那么A线程和B线程的优先级一样

    优先级具有规则性和随机性

    优先级具有规则性指的是:CPU会尽量将执行资源让给优先级比较高的,但是不是完全的。
    优先级具有随机性指的是:优先级高的线程不代表任务一定能执行完,也有可能优先级低的任务先执行完了。但是大概率会是优先级高的会优先执行完。

    相关文章

      网友评论

          本文标题:五、Java高级特性(多线程基础使用篇)

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