• 三种创建线程的方式详解。


    1.继承thread类

    2.实现runnable接口。

    3.实现callable接口。

    这三种方式具体如何创建如何调用详见下面代码,注释已经写得很详细了。例子中分别用这三种方式创建线程并分别启动了两个线程。具体区别自己细微体会下。

    package com.thread.Lone;
    
    /**
     * 创建线程的第一种方法
     * 继承java.lang.Thread,然后重写该类的run方法.
     */
    public class ThreadExt extends java.lang.Thread {
    
            public void run(){
                System.out.println(Thread.currentThread().getName()+" start");
                for(int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+"  : "+i);
                }
                System.out.println(Thread.currentThread().getName()+" end");
            }
    
    }
    package com.thread.Lone;
    
    /**
     * 第二种创建线程的方法,实现Runnable接口
     * 因为java不支持多继承,那么假设你的类本身继承了一个父类
     * 这时候还想创建线程的话,那么实现java.lang.Runnable就比较合适了。
     */
    public class ThreadRunnable implements java.lang.Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" start");
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"  : "+i);
            }
            System.out.println(Thread.currentThread().getName()+" end");
        }
    }
    package com.thread.Lone;
    
    import java.util.concurrent.Callable;
    
    /**
     * 前面两种方式,都没有返回值,那如果我们希望线程能把执行结果返回到主程序,就需要用到concurrent包里面的东西
     * java.util.concurrent.FutureTask 和 java.util.concurrent.Callable
     * 这里需要实现Callable接口,主要是为了实现call()方法,而call方法就是带有返回值的。
     * 启动方式见主程序。
     */
    public class ThreadFuture implements Callable{
        @Override
        public Object call() throws Exception {
    
            System.out.println(Thread.currentThread().getName()+" start");
            int i;
            for(i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"  : "+i);
            }
            System.out.println(Thread.currentThread().getName()+" end");
            //为了体现Future的get()的等待效果,这里休息2秒
            Thread.sleep(2000);
            return Thread.currentThread().getName()+" 总共循环了"+i+"次";
        }
    }
    package com.thread.Lone;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * 在这个类里面我们会分别启动不同方式创建的线程。
     */
    public class TestRun {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            //  启动继承Thread类的线程
            //  执行这段代码你可以发现console打出的log基本每次都不一样
            //  甚至有时候ext2的log会输出在ext1之前,可以看出线程启动之后,具体的执行已经和主程序无关,也不会受到其他线程的影响。
            ThreadExt ext1 = new ThreadExt();
            //这里有一个好习惯就是,对于你启动的线程最好命名一下方便排查问题。
            ext1.setName("ThreadExt one");
            ThreadExt ext2 = new ThreadExt();
            ext2.setName("ThreadExt two");
            //通过阅读源码可以发现,最终调用start()方法是去调用一个本地的start0方法
            //private native void start0(); 由于我不懂C也就不去看了。
            ext1.start();
            ext2.start();
    
            //下面启动实现runnable接口的线程
            //启动方法略有不同,因为runnable接口本身是没有start()方法的。
            //所以要启动方式是,新建一个Thread对象并把自己的对象当做Target传进去
            //而在Thread的run()方法的注释里面有这么写到:当构建Thread的时候target不为空,调用target的Run,而如果为空,则什么都不做。
            ThreadRunnable threadRunnable1 = new ThreadRunnable();
            //这里可以使用带名字的构造函数
            Thread t1 = new Thread(threadRunnable1,"threadRannable1 ");
            t1.start();
            //匿名方式
            new Thread(threadRunnable1,"threadRannable2 ").start();
    
            //下面启动实现Callable的线程
            //和实现Runnable的线程类似,最终的启动方式也是通过Thread.start来启动的。
            //但是Thread只接受Runnalbe类型的target,而我们的类实现的是callable接口,明显不匹配
            //这时候就需要引入一个FutureTask类,这个类本身实现了runnable但是持有一个callable对象。是一个典型的适配器模式,将一个Callable对象伪装成一个Runnable对象
            ThreadFuture threadFuture  = new ThreadFuture();
            //把threadFuture给传入到futureTask
            FutureTask futureTask = new FutureTask(threadFuture);
            //这时候,我们就可以把持有threadFuture对象的FutureTask当做target传给Thread对象。
            //通过源码可以看到,当start()执行的时候,会走到futureTask的Run方法,而futureTask的run方法,又回去调用ThreadFuture的call方法,并且把返回值保存到FutureTask的outcome属性上。
             new Thread(futureTask,"ThreadFuture1 ").start();
            //当我们调用get()的时候,其实会去判断当前的状态,如果线程已经跑完(state=1),就拿到outCome给我们,而如果没有跑完就会等待跑完。
             System.out.println(futureTask.get());
            //所以如果单独调ThreadFuture相关代码的话,就可以看到不同,这里在ThreadFuture1执行完之前是不会执行ThreadFuture2的,因为get会处在等待状态,一直等到当前线程执行完成才会继续走下面的逻辑.
            //另外需要注意的是,futureTask在线程执行完之后,本身的状态已经变成Normal了,这时候再次启动同一个futureTask的时候,实际上是什么都不做的。
            // 例如:new Thread(futureTask,"ThreadFuture2 ").start(); 其实并不会走到ThreadFuture
            //想要新启动一个线程需要new一个futureTask。
            FutureTask futureTask2 = new FutureTask(threadFuture);
            new Thread(futureTask2,"ThreadFuture2 ").start();
            System.out.println(futureTask2.get());
    
        }
    }
  • 相关阅读:
    SQL Server中生成指定长度的流水号
    属性与字段的区别
    Tomcat启动时的异常~!!!
    全面认识验证身份的数字证书
    MyEclipse 5.5 开发 Spring + Struts + Hibernate 的详解视频(长1.5小时)
    resin是什么?是Application Server吗?是WebLogic,Websphere他们的竞争对手吗?
    发现一个HTML得秘密
    用 MyEclipse 开发的最简单的 Spring 例子
    什么是WebSphere?WebSphere是干什么用的?中间件是什么意思?
    简单jsp+servlet实例
  • 原文地址:https://www.cnblogs.com/my-king/p/7810583.html
Copyright © 2020-2023  润新知