java中多线程的开发中少不了使用Thread,咱们在使用Thread中提供的API过程当中,应该注意些什么规则呢?java
一块儿来看一看吧。git
Thread中有两个方法,一个是start方法,一个是run方法,两个均可以调用,那么两个有什么区别呢?github
先看一下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()方法。markdown
若是咱们直接调用Thread的run()方法会发生什么事情呢?多线程
先看一下run方法的定义:函数
public void run() {
if (target != null) {
target.run();
}
}
复制代码
默认状况下, 这个target就是一个Runnable对象,若是Thread是经过Runnable来构建的话,调用Thread.run()会在当前线程中运行run方法中的内容。oop
若是Thread是以其形式构建,而且没有从新run()方法,那么直接调用Thread.run()将什么都不会作。this
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();
}
复制代码
因此,上面两种调用方式,只有第二种是正确的。
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中活动的线程个数,这个方法会统计还未启动的线程,同时也会受系统线程的影响,因此是不许确的。
这个方法是将ThreadGroup和子group的线程拷贝到一个数组中,可是若是数组过小了,多余的线程是会被自动忽略的。
ThreadGroup自己有一个 stop() 方法用来中止全部的线程,可是stop是不安全的,已经被废弃了。
那么咱们该怎么去安全的中止不少个线程呢?
使用executor.shutdown()就能够了。
刚刚讲了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()的使用要点:
若是当前线程实例在调用Object类的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在该实例中调用了Thread.sleep(long)或Thread.sleep(long,int)方法,而且正在阻塞状态中时,则其中断状态将被清除,并将收到InterruptedException。
若是此线程在InterruptibleChannel上的I/O操做中处于被阻塞状态,则该channel将被关闭,该线程的中断状态将被设置为true,而且该线程将收到java.nio.channels.ClosedByInterruptException异常。
若是此线程在java.nio.channels.Selector中处于被被阻塞状态,则将设置该线程的中断状态为true,而且它将当即从select操做中返回。
若是上面的状况都不成立,则设置中断状态为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不是被错误的被唤醒,因此咱们须要在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
本文已收录于 www.flydean.com/java-securi…
最通俗的解读,最深入的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注个人公众号:「程序那些事」,懂技术,更懂你!