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

2021-05-28  本文已影响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会尽量将执行资源让给优先级比较高的,但是不是完全的。
优先级具有随机性指的是:优先级高的线程不代表任务一定能执行完,也有可能优先级低的任务先执行完了。但是大概率会是优先级高的会优先执行完。

上一篇下一篇

猜你喜欢

热点阅读