Thread类详解:
1.线程(Thread): 是操作系统进行调度的最小单位,Java中的线程是对操作系统线程的封装。本文从线程的创建到停止结合代码和具体实例分析一下关于java线程的一些常见问题。
2.线程的创建:
(1)自己写一个类继承于java.lang.Thread类,并重写run()接口
(2)实现java.lang.Runnable接口,并传给Thread的构造函数。
//方式1 class MyThread extends Thread{ @Override public void run() { //to do something in this thread } } //new一个Thread对象 Thread thread = new MyThread(); //方式2 class MyRunnable implements Runnable{ @Override public void run() { //to do something in this thread } } //new一个线程对象 Thread thread = new Thread(new MyRunnable());
3.线程的启动: 线程对象提供了一个start方法,调用了start方法后,线程将被操作系统调度执行,执行时JVM会调用线程的run方法。值得注意的是:start方法不能重复调用,举个例子:
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("1"); } }); for(int i=0;i<10;i++){ thread.start(); }
4.线程中断:线程对象提供了一个interrupt方法,可以对一个已经启动的线程进行中断操作。对一个线程执行interrupt操作后,有如下四种结果:
(1)如果被中断的线程正处于阻塞状态:包括阻塞于Object.wait(),Thread.join(),Thread.sleep()等方法,那么该线程将收到一个InterruptedException,同时线程的中断状态将被设置为false;
(2)如果被中断的线程阻塞于java.nio.channels.InterruptibleChannel的IO操作,那么channel将被关闭,并且该线程将的中断状态将被设置为true,同时该线程将收到一个java.nio.channels.ClosedByInterruptException;
(3)如果被中断的线程阻塞于java.nio.channels.Selector,那么阻塞的方法将立即返回,同时线程的中断状态将被设置为true;
(4)如果不是以上3种情况,那么被中断线程的中断状态将被设置为true。
Thread thread1 = new Thread(new Runnable() { @Override public void run() { int i=0; //调用interrupt()后,循环会退出吗?答案是肯定会的 while (!Thread.currentThread().isInterrupted()){ System.out.println(String.format("线程名称:[%s],执行第:[%s]次循环",Thread.currentThread().getName(),i+1)); i++; } } },"thread1"); thread1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //中断线程 thread1.interrupt();
Thread thread1 = new Thread(new Runnable() { @Override public void run() { int i=0; //调用interrupt()后,循环会退出吗?答案是不会 while (!Thread.currentThread().isInterrupted()){ System.out.println(String.format("线程名称:[%s],执行第:[%s]次循环",Thread.currentThread().getName(),i+1)); i++; try { Thread.sleep(100);//状态会被清理,循环不会终止 } catch (InterruptedException e) { e.printStackTrace(); System.out.println(String.format("[%s] is interrupted",Thread.currentThread().getName())); } } System.out.println(String.format("线程名称:[%s],执行结束",Thread.currentThread().getName())); } },"thread1"); thread1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } thread1.interrupt();
5.线程休眠:Thread.sleep()方法,该方法是让当前线程休息一定的时间后,再继续执行。需要指出的是,在执行该方法后,当前线程并不会释放锁,在高并发的场景下可能会导致其他线程无法获取锁从而造成性能问题,因此要慎用该方法。
6.等待线程执行完毕:在一个线程中等待另一个线程执行完毕,只需要调用线程对象的join()方法即可
Thread thread1 = new Thread(new Runnable() { @Override public void run() { for (int i=0;i<10;i++){ System.out.println(String.format("线程名称:[%s],执行第:[%s]次循环",Thread.currentThread().getName(),i+1)); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } },"thread1"); thread1.start(); try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } //等thread1执行完毕,才会执行到这里 System.out.println("main thread finished successfully");
7.线程的停止:
当线程的run()方法执行完毕,线程会自然的停止。那么问题来了,怎么让一个运行状态中的线程停止呢?JDK中的Thread提供了一个静态方法stop(),不过目前已经废弃了。因为stop方法在线程停止的同时会让线程释放已经获取的锁,这会导致其他排队等待锁的线程获取锁,从而导致不可预知的情况。所以,官方已经不建议使用Thread.stop()方法了。那么,停止线程的正确姿势是什么?JDK中建议用一个自定义的变量用于标识线程是否需要退出,然后线程不断地去检查该变量从而决定要不要从run方法退出;或者调用线程对象的interrupt方法,通过中断异常来退出执行。
8.线程安全:
共享变量: 多个线程都能访问的变量,通常是静态变量或类的成员变量。为啥这两类变量是共享的啊?因为他们存放在JVM的堆上,JVM堆上的内存区域是多个线程共享的。