Java并发编程—认识Java里的线程

并发编程—认识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());
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_38345031/article/details/84104257