关于java:java安全编码指南之Thread-API调用规则

45次阅读

共计 3943 个字符,预计需要花费 10 分钟才能阅读完成。

简介

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/

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

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

正文完
 0