java线程

2020-10-18  本文已影响0人  傻明蚕豆

大家好,我是傻明蚕豆,今天为大家带来java线程的基础知识。

我在CSDN也发布了,编辑比这里美观:https://blog.csdn.net/weixin_44668634/article/details/109147514

一、线程的概念;

进程:进程就是一个在内存中运行的应用程序,比如你电脑在运行的一个QQ,如果你再打开个哭狗,那就是另一个进程,每个进程都有自己的独立内存空间,一个进程中可以有多个线程。

线程:线程是进程里面的一个执行流程,是CPU调度和分派的基本单位,一个进程中可以有多个线程,线程与进程内的其他线程一起共享所有该进程的资源,每个线程有自己的堆栈和局部变量。java程序中最少有两个线程,一个main线程,一个是垃圾回收线程。

二、线程的生命周期;

NEW状态:

一个已创建而未启动(还没调用Thread.start()方法)的Java线程,就是处于NEW状态。

RUNNABLE状态:

当线程调用了start()之后,就会进入RUNNABLE状态,RUNNABLE状态包含两个子状态:READY和RUNNING。

READY:准备状态,当被线程调度器进行调度,会变为RUNNING状态。

RUNNING:运行状态,线程正在运行,run()方法中的代码在执行。

当Java线程调用yield()方法时或者由于线程调度器的调度,线程的状态有可能由RUNNING转变为READY,Thread.getState()可以获取到状态。

WAITING状态:

无限期等待状态,需要唤醒,进入该状态是由于调用了下面方法之一:不带超时的Object.wait(),不带超时的Thread.join(),LockSupport.park() 。

TIMED WAITING状态:

时间等待状态,无需唤醒,进入该状态是由于调用了下面方法之一:Thread.sleep(time),Object.wait(time),Thread.join(time),LockSupport.parkNanos(time),LockSupport.parkUntil(time)

BLOCKED状态:

阻塞状态,遇到阻塞的I0操作或者在等待某个锁资源都会进入阻塞状态。

TERMINATED状态:

死亡状态,线程的最终状态,在该状态的线程不会再切换到其它任何状态,意味着线程的整个生命周期都结束了。

三、线程的创建方式;

1、继承Thread类

public class Thread1 extends Thread{

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName());

    }

    public static void main(String[] args) {

        Thread t=new Thread1();

        t.start();

        //t.start();

    }

}

2、实现Runnable接口

public class Thread2 implements Runnable{

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName());

    }

    public static void main(String[] args) {

        new Thread(new Thread2()).start();

    }

}

3、实现Callable接口

public class Thread3 implements Callable{

    public static void main(String[] args) {

        ExecutorService executorService=Executors.newFixedThreadPool(1);

        Thread3 t=new Thread3();

        Future future=executorService.submit(t);

        System.out.println("result:"+future);

    }

    @Override

    public Object call() throws Exception {

        String threadName=Thread.currentThread().getName();

        System.out.println("name:"+threadName);

        return threadName;

    }

}

四、线程的启动和停止;

怎么启动线程?

Thread类实现了Runnable接口,所以创建线程继承Thread类或者实现Runnable接口都是一样的,因为java是单继承,所以如果要继承其他类,那就只有实现Runnable接口了。

线程启动必须调用Thread.start方法,run()方法只是一个类中的普通方法,调用run方法跟调用其他普通方法一样,而start()是会创建新线程,然后新线程调用run方法。

我们来看看Thread类的源码(JDK1.8):

private volatile int threadStatus = 0;

public synchronized void start() {

if (threadStatus != 0){

        throw new IllegalThreadStateException();

    }

    group.add(this);

    boolean started = false;

    try {

        start0();

        started = true;

    } finally {

        try {

            if (!started) {

                group.threadStartFailed(this);

            }

        } catch (Throwable ignore) {

            /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */

        }

    }

private native void start0();

public enum State {

NEW,

RUNNABLE,

BLOCKED,

WAITING,

TIMED_WAITING,

TERMINATED;

}

threadStatus和枚举类State就是用来记录Thread的状态,Thread类中并没有对threadStatus进行过赋值,那么threadStatus应该是在start0方法里面改变了。

start0方法是native修饰的,所以是调用了C++实现的方法,而threadStatus是volatile修饰,保证可见性,

因此native方法进行变量threadStatus修改的同时Thread类中就能同时获取到该变量的最新值。

同时start方法是synchronized修饰的,所以即使多线程同时调用start方法,也能保证原子性。

线程初始化后状态为0,所以start方法只可以调用一次,因为线程调用过一次start方法后,状态就会改变,那么threadStatus就不等于0了,

由于threadStatus是在c++里面修改,所以看不到ThreadStatus等于多少,但肯定不等于0,所以再调用start方法就会抛出IllegalThreadStateException。

如何中断线程?

不要问我为什么不用stop方法。

在Thread线程类里面有个isInterrupted方法,

private native boolean isInterrupted(boolean ClearInterrupted);

是一个native方法,Thread类有两个public方法调用了该native方法,

public boolean isInterrupted() {

    return isInterrupted(false);

}

这个方法只返回线程的中断状态,并没有去改变线程的中断状态。

public static boolean interrupted() {

    return currentThread().isInterrupted(true);

}

这个方法是个静态方法,所以只作用于当前正在执行的线程,方法里面调用了currentThread()获取当前线程,该方法返回当前线程的中断状态,并且会重置线程的中断状态。

这两个方法都只是查看线程的中断状态,并没有中断线程,还有一个方法interrupt():

public void interrupt() {

    if (this != Thread.currentThread())

        checkAccess();

    synchronized (blockerLock) {

        Interruptible b = blocker;

        if (b != null) {

            interrupt0();          // Just to set the interrupt flag

            b.interrupt(this);

            return;

        }

    }

    interrupt0();

}

这个就是中断线程的方法,首先判断是否是当前线程,如果不是调用checkAccess()方法,大概就是查询权限吧,也就是说一个线程只允许自己中断。

我们再看看这个方法的注释,调用interrupt方法时,如果线程因为以下方法的调用而处于阻塞中,线程的中断标志会被清除,并抛出一个InterruptedException。

以下方法包括:Object类的wait()、wait(long)、wait(long, int)和Thread类的join()、join(long)、join(long, int)、sleep(long)、sleep(long, int)。

如果线程没有因为以上方法调用而进入阻塞状态的话,那么中断这个线程仅仅会设置它的中断标志位,而不会抛出InterruptedException。

总结:调用interrupt方法,并不会中断一个正在运行的线程,而是将线程的中断标志设为true,在某些情况下抛出一个InterruptedException罢了。

也就是说,无论是设置中断状态,还是抛出InterruptedException,那都是给当前线程的建议,至于在收到这些中断的建议后,当前线程要怎么处理,完全取决于当前线程。

那么到底怎么中断线程呢?

设置中断标记法

public static void test1(){

    Thread thread=new Thread(()->{

        System.out.println("线程启动了");

        while(!Thread.currentThread().isInterrupted()){ //默认情况下isInterrupted() 返回 false

            System.out.println(Thread.currentThread().isInterrupted());

        }

        System.out.println("线程结束了");

    },"test1");

    thread.start();

    try {

        Thread.sleep(2);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    //通过调用interrupt(),把while里面的isInterrupted()返回变为true

    thread.interrupt();

}

当main线程运行到最后一行代码thread.interrupt();,那么Thread.currentThread().isInterrupted()就等于true,

然后循环不成立,接着执行输出"线程结束了",那岂不是没法中断?所以逻辑代码都写在循环里面才能使用中断标记法。

捕获InterruptedException法

public static void test2(){

    Thread thread=new Thread(()->{

        System.out.println("线程启动了");

        while(!Thread.currentThread().isInterrupted()){

            try {

                Thread.sleep(1100);

                System.out.println(Thread.currentThread().isInterrupted());//逻辑处理代码

            } catch (InterruptedException e) {

                System.out.println("出异常了");

                //Thread.currentThread().interrupt();//调用中断方法

                //break;//跳出循环

                //return ;//跳出方法,不再执行输出"线程结束了"的逻辑

            }

        }

        System.out.println("线程结束了");

    },"test2");

    thread.start();

    try {

        Thread.sleep(1000 * 5);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    //线程执行了一段时间(上面睡眠5秒),我突然想要线程停止,于是调用interrupt方法

    thread.interrupt();

}

这里如果线程test2正在sleep,main线程这时候执行thread.interrupt(),那么会抛出异常,并且线程的中断标志会被清除,所以要做什么操作,就必须在catch里面搞了。

或者这样写:

public static void test3(){

    Thread thread=new Thread(()->{

        System.out.println("线程启动了");

        try {

            while(!Thread.currentThread().isInterrupted()){

                Thread.sleep(1100);

                System.out.println(Thread.currentThread().isInterrupted());//逻辑处理代码

            }

        } catch (InterruptedException e) {

            System.out.println("出异常了");

            //return ;//跳出方法,不再执行输出"线程结束了"的逻辑

        }

        System.out.println("线程结束了");

    },"test3");

    thread.start();

    try {

        Thread.sleep(1000 * 5);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    thread.interrupt();

}

五、线程的其他方法;

sleep方法:

public static native void sleep(long millis) throws InterruptedException;

sleep是一个静态的native方法,它作用于当前线程,当调用sleep方法后,当前线程会让出CPU资源,但不会释放锁。

Thread.sleep() 与 Thread.currentThread().sleep() 是一样的,只不过一个是用类直接调用静态方法, 一个是用类的实例调用静态方法。

yield方法:

public static native void yield();

yield是一个静态的native方法,当调用yield方法后,当前线程表示会让出CPU,但到底让不让是随机的,或者是CPU决定的。

join方法:

调用join方法后,会让当前线程先执行,如果指定时间,则先执行指定时间,如果时间为0,则是让当前线程执行完为止。

比如有线程T1和线程T2,线程T1里面有这样代码:T2.jion(0);

那么T1会立刻停止让线程T2执行,一直到T2执行完,T1才会继续执行,即使T2里面调用了sleep方法让出CPU资源,T1也会等T2执行完才能获得CPU资源。

我们来看一下源码:

public final synchronized void join(long millis) throws InterruptedException {

    long base = System.currentTimeMillis();

    long now = 0;

    if (millis < 0) {

        throw new IllegalArgumentException("timeout value is negative");

    }

    //当参数为0,while循环会一直检查T2是否存活,存活的话T1就一直wait(0)。

    //注意:如果T1在执行T2.jion(0)时,T2还没start,那么T2就不存活,那么T2.jion(0)相当于白写了。

    if (millis == 0) {

        while (isAlive()) {

            wait(0);

        }

    } else {

        while (isAlive()) {

            long delay = millis - now;

            if (delay <= 0) {

                break;

            }

            wait(delay);

            now = System.currentTimeMillis() - base;

        }

    }

}

好了,java线程就介绍到这里,如有问题请留言一起探讨。

谢谢观看!

上一篇下一篇

猜你喜欢

热点阅读