并发编程—认识Java里的线程
Java程序天生就是多线程的。
Java程序天生就是多线程的。看例子:
/**
* Java语言天生就是多线程的
*/
@Test
public void javaThreadMX(){
// 虚拟机线程管理接口
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 得到所有的线程信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false);
// 循环打印出运行线程的id和name
Arrays.stream(threadInfos).forEach(i -> {
System.out.println("[" + i.getThreadId() + "]--" + i.getThreadName() );
});
}
上面这段代码运行的结果:
* result:运行的结果
* [6]--Monitor Ctrl-Break
* [5]--Attach Listener
* [4]--Signal Dispatcher
* [3]--Finalizer
* [2]--Reference Handler
* [1]--main
从结果中可以看出我们明明只运行了一个自己写的JavaThreadMX()方法,却有6个线程创建和执行。说明了Java程序本身就是多线程的。
创建/启动新线程
在Java中创建新线程有三种方式, 分别为:Thread(Class)、Runnable(Interface)、Callable(Interface)。
三者的区别:
- 大家都知道Thread和Runnable的区别就是一个是Class 一个是Interface【废话】,至于为什么有了Thread为啥又出现个Runnable呢?答案就是:Java是单继承的,可以多实现的。
- Runnable和Callable,两者都是接口,启动方式都是交给Thread来启动,区别就是实现Callable接口的任务是可以有返回值的,而Runnable是没有返回值的,第二就是,Callable是可以向上抛出异常的,而Runnableh只能自己消化。Callable的使用不是很多,和FutureTask一起使用,使用FutureTask中的get() 方法来获取返回值,注意的是:使用get() 方法会阻塞主线程,不使用当然是不会阻塞的。【这里对Runnable和Callable简单的说明,其实这里的东西很多的,后边慢慢学习】
举个栗子
继承Thread类
/**
* 继承Thread创建新线程
*/
public class NewThread_Thread extends Thread{
/**
* 重写run()方法
*/
@Override
public void run(){
System.out.println("I`m extends thread class.");
}
}
/**
* 启动线程
*/
@Test
public void startThread(){
// 继承Thread类线程的启动方式,调用start()方法
new NewThread_Thread().start();
}
实现Runnable接口
/**
* 实现Runnable接口创建新线程
*/
public static class NewThread_Runnable implements Runnable{
/**
* 重写run() 方法
*/
@Override
public void run() {
System.out.println("I`m implement runnable interface.");
}
}
/**
* 启动线程
*/
@Test
public void startThread(){
// 实现Runnable接口 启动【交给Thread类启动】
new Thread(new NewThread_Runnable()).start();
}
实现Callable接口
/**
* 实现Callable接口创建新线程
* Callable需要指定泛型【返回值的类型】
*/
public static class NewThread_Callable implements Callable<String>{
/**
* 重写call()方法
*/
@Override
public String call() throws Exception {
System.out.println("I`m implement callable interface.");
// 设置返回值
return "Call result!";
}
}
/**
* 启动线程
*/
@Test
public void startThread(){
// 实现Runnable接口 启动
NewThread_Callable callable = new NewThread_Callable();
// 将Callable的实现类交给FutureTask
FutureTask<String> futureTask = new FutureTask<>(callable);
// FutureTask实现了Runnable接口,故启动方式和Runnable一样
new Thread(futureTask).start();
try {
// 获取线程返回值
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
注意:在启动线程时调用的是start()方法,而不是run()方法。
run() 和 start() 的区别:
调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
记住一点,run()方法只是Thread类中的普通方法,不能够启动创建的新线程。
中断/终止线程
怎么样才能让Java里的线程安全停止工作呢?
线程中断/停止情况:
- 线程自然终止:自然执行完或抛出未处理异常
- stop(),resume(),suspend()已不建议使用,stop()会导致线程不会正确释放资源,suspend()线程挂起,不释放资源,容易导致死锁。
- interrupt()方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
- static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false。
方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。 - isInterrupted() 判定当前线程是否处于中断状态。
举个栗子
interrupt()中断线程:
public static class EndThreadByInterrupt extends Thread {
@Override
public void run() {
// isInterrupted() : 线程是否中断, 中断:true
// isInterrupted()是Thread类中的方法,实现Runnable接口时,使用Thread.currentThread().isInterrupted()
while (!isInterrupted()){
System.out.println("线程" + Thread.currentThread().getId() + "is running .");
}
System.out.println("中断标志位:" + isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
// 启动线程
EndThreadByInterrupt endThreadByInterrupt = new EndThreadByInterrupt();
endThreadByInterrupt.start();
// 线程休眠200ms
Thread.sleep(200);
// 中断,将中断标志位【isInterrupted()】改为true
endThreadByInterrupt.interrupt();
}
方法中抛出InterruptedException异常的话需要在catch语句块中再次掉用interrupt()方法中断
public static class EndThreadInterruptException extends Thread {
@Override
public void run() {
// isInterrupted() : 线程是否中断, 中断:true
// isInterrupted()是Thread类中的方法,实现Runnable接口时,使用Thread.currentThread().isInterrupted()
while (!isInterrupted()) {
try {
Thread.sleep(500);
System.out.println("线程" + Thread.currentThread().getId() + "is running .");
} catch (InterruptedException e) {
// 这里出现InterruptException异常,会将isInterrupted()的值改为false
System.out.println("中断标志位:" + isInterrupted());
// 打印异常
e.printStackTrace();
// 再次调用中断方法,也就是将isInterrupted()的值改为true
interrupt();
}
}
System.out.println("中断标志位:" + isInterrupted());
}
}