简介

java中多线程的开发中少不了应用Thread,咱们在应用Thread中提供的API过程中,应该留神些什么规定呢?

一起来看一看吧。

start一个Thread

Thread中有两个办法,一个是start办法,一个是run办法,两个都能够调用,那么两个有什么区别呢?

先看一下start办法:

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) {            }        }    }    private native void start0();

start()是一个synchronized的办法,通过它会去调用native的start0办法,而最终将会调用Thread的run()办法。

咱们晓得,创立一个Thread有两种形式,一种是传入一个Runnable,一个是继承Thread,并重写run()办法。

如果咱们间接调用Thread的run()办法会产生什么事件呢?

先看一下run办法的定义:

    public void run() {        if (target != null) {            target.run();        }    }

默认状况下, 这个target就是一个Runnable对象,如果Thread是通过Runnable来构建的话,调用Thread.run()会在以后线程中运行run办法中的内容。

如果Thread是以其模式构建,并且没有从新run()办法,那么间接调用Thread.run()将什么都不会做。

    public void wrongStart(){        Runnable runnable= ()-> System.out.println("in thread running!");        Thread thread= new Thread(runnable);        thread.run();    }    public void correctStart(){        Runnable runnable= ()-> System.out.println("in thread running!");        Thread thread= new Thread(runnable);        thread.start();    }

所以,下面两种调用形式,只有第二种是正确的。

不要应用ThreadGroup

Thread中有个字段类型是java.lang.ThreadGroup,这个次要是用来给Thread进行分组,咱们看下Thread的这个构造函数:

 public Thread(ThreadGroup group, Runnable target) {        this(group, target, "Thread-" + nextThreadNum(), 0);    }

下面的构造函数能够在传入runnable的同时传递一个ThreadGroup对Thread进行分组。

如果没有指定ThreadGroup,那么将会为其调配一个默认的default group。

ThreadGroup是做什么的呢?ThreadGroup是java 1.0引入的办法,次要是一次性的对一组thread进行操作。咱们能够调用ThreadGroup.interrupt()来一次性的对整个Group的Thread进行interrupts操作。

尽管ThreadGroup提供了很多有用的办法,然而其中很多办法都被废除了,比方:allowThreadSuspension(), resume(), stop(), 和 suspend(),并且ThreadGroup中还有很多办法是非线程平安的:

  • ThreadGroup.activeCount()

这个办法次要是用来统计一个ThreadGroup中流动的线程个数,这个办法会统计还未启动的线程,同时也会受零碎线程的影响,所以是不精确的。

  • ThreadGroup.enumerate()

这个办法是将ThreadGroup和子group的线程拷贝到一个数组中,然而如果数组太小了,多余的线程是会被主动疏忽的。

ThreadGroup自身有一个 stop() 办法用来进行所有的线程,然而stop是不平安的,曾经被废除了。

那么咱们该怎么去平安的进行很多个线程呢?

应用executor.shutdown()就能够了。

不要应用stop()办法

刚刚讲了ThreadGroup中不要调用stop()办法,因为stop是不平安的。

调用stop办法会立马开释线程持有的所有的锁,并且会抛出ThreadDeath异样。

因为会开释所有的锁,所以可能会造成受这些锁爱护的对象的状态产生不统一的状况。

代替的办法有两种,一种是应用volatile flag变量,来控制线程的循环执行:

    private volatile boolean done = false;    public void shutDown(){        this.done= true;    }    public void stopWithFlag(){        Runnable runnable= ()->{            while(!done){                System.out.println("in Runnable");            }        };        Thread thread= new Thread(runnable);        thread.start();        shutDown();    }

另外一种办法就是调用interrupt(), 这里咱们要留神interrupt()的应用要点:

  1. 如果以后线程实例在调用Object类的wait(),wait(long)或wait(long,int)办法或join(),join(long),join(long,int)办法,或者在该实例中调用了Thread.sleep(long)或Thread.sleep(long,int)办法,并且正在阻塞状态中时,则其中断状态将被革除,并将收到InterruptedException。
  2. 如果此线程在InterruptibleChannel上的I/O操作中处于被阻塞状态,则该channel将被敞开,该线程的中断状态将被设置为true,并且该线程将收到java.nio.channels.ClosedByInterruptException异样。
  3. 如果此线程在java.nio.channels.Selector中处于被被阻塞状态,则将设置该线程的中断状态为true,并且它将立刻从select操作中返回。
  4. 如果下面的状况都不成立,则设置中断状态为true。

先看上面的例子:

    public static void main(String[] args)  {        Runnable runnable= ()->{            while (!Thread.interrupted()) {             System.out.println("in thread");            }        };        Thread thread= new Thread(runnable);        thread.start();        Thread.sleep(5000);        thread.interrupt();    }

咱们在while循环中调用了Thread.interrupted()办法用来判断线程是否被设置了中断位,而后在main办法中调用了thread.interrupt()来设置中断,最终能够正确的进行Thread。

留神,这里运行的Thread并没有被阻塞,所以并不满足咱们下面提到的第一个条件。

上面咱们再看一个例子:

    public static void main(String[] args)  {        Runnable runnable= ()->{            while (!Thread.interrupted()) {             System.out.println("in thread");                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        };        Thread thread= new Thread(runnable);        thread.start();        thread.interrupt();    }

这个例子和下面的例子不同之处就是在于,Thread中调用了sleep办法,导致Thread被阻塞了,最终满足了第一个条件,从而不会设置终端位,只会抛出InterruptedException,所以这个例子中线程是不会被进行的,大家肯定要留神。

wait 和 await 须要放在循环中调用

为什么要放在循环中呢?因为咱们心愿wait不是被谬误的被唤醒,所以咱们须要在wait被唤醒之后,从新检测一遍条件。

谬误的调用是放在if语句中:

synchronized (object) {  if (<condition does not hold>) {    object.wait();  }  // Proceed when condition holds}

正确的办法是放在while循环中:

synchronized (object) {  while (<condition does not hold>) {    object.wait();  }  // Proceed when condition holds}

本文的代码:

learn-java-base-9-to-20/tree/master/security

本文已收录于 http://www.flydean.com/java-security-code-line-thread/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!