• 线程创建与启动


    什么是线程?

    可以这么说:操作系统可以同时执行多个任务,每个任务是进程,进程可以执行多个任务,每个任务就是线程。

    线程的优势

    1、进程之间不能共享内存,但线程之间共享内存非常容易。
    2、系统创建进程是需要为该进程 重新分配系统资源,但是创建线程代价小得多。
    3、java语言内置了多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化了java的多线程开发。

    线程的创建

    1、通过继承Thread类创建线程类

    (a)定义Thread子类,并重写该类的run()方法。该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行体
    (b)创建Thread子类的实例,即创建了线程对象。
    (c)调用线程的start()方法来启动线程。

    示例:
    package czz;
    
    public class FirstThread extends Thread {
        private int i;
        //重写run()方法,run()方法的方法体就是线程的执行体
        @Override
        public void run(){
            for (;i<300;i++){
                //当线程继承Thread类时,直接使用this获取当前线程,Thread对象的getName()方法返回当前线程的名字
                //因此可以直接调用getName()方法返回当前现成的名字
                System.out.println(getName() + " " + i );
            }
        }
    
        public static void main(String[] args) {
            for (int i=0;i<300;i++){
                //调用Thread的currentThread()方法获取当前线程(本例为主线程)
                System.out.println(Thread.currentThread().getName() + " " + i);
                if (i==10){
                    //创建并启动第一个线程
                    Thread jdwa = new FirstThread();
                    //为线程设置名字
                    jdwa.setName("jdwa");
                    jdwa.start();
                    //创建并启动第一个线程
                    Thread czz = new FirstThread();
                    czz.setName("czz");
                    czz.start();
    
                }
            }
        }
    }
    
    
    

    结果:

    "D:...jdk1.8.0_05injava.exe" "-javaagent:D:...IntelliJ IDEA 
    ...  ...
    FilesJavajdk1.8.0_05jrelib
    t.jar;E:...outproductionMyTest" czz.FirstThread
    main 0
    ... ...
    main 80
    czz 0
    ... ...
    czz 126
    main 81
    ... ...
    main 174
    jdwa 0
    jdwa  1
    main 175
    ... ...
    main 299
    czz  127
    ... ...
    czz  210
    jdwa  2
    ... ...
    jdwa  22
    czz  211
    ... ...
    czz 299
    jdwa  23
    ... ...
    jdwa  299
    
    Process finished with exit code 0
    
    

    有上面的示例可以看出,两个线程的循环变量不连续表明他们没有共享数据,因为每次创建线程都需要创建一个Thread对象,因此:
    使用Thread类的方法创建线程时,多个线程之间无法共享线程类的实例变量。

    2、实现Runnable接口创建线程类

    (a)定义Runnable接口的实现类,并重写改接口的run()方法,该run()方法的方法体同样是该线程的执行体。
    (b)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。代码如下所示:

    //创建Runnable实现类的实例
    SecondThreadst = new SecondThread();
    //以Runable实现类的实例作为Thread的target来创建Thread的对象,即线程对象。
    new Thread(st);
    

    也可以在创建对象时为该Thread对象指定一个名字:

    new Thread(st,"threadName");
    

    (c)调用线程对象的start()方法来启动该线程
    示例:

    package czz;
    
    public class SecondThread implements Runnable {
        private int i;
        @Override
        public void run(){
            for (;i<300;i++){
                //当线程类实现Runnable接口时,获取当前线程需要Thread.currentThread()
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    
        public static void main(String[] args) {
            for (int i=0;i<12;i++){
                //调用Thread的currentThread()方法获取当前线程(本例为主线程)
                System.out.println(Thread.currentThread().getName() + " " + i);
                if (i==10){
                    SecondThread target = new SecondThread();
                    //通过New Thread(target,name)方法创建新线程
                    new Thread(target,"jdwa").start();
                    new Thread(target,"czz").start();
                }
            }
        }
    }
    
    

    3、使用Callable和Future创建线程

    java5提供了Callable接口,这个接口类似于Runnable接口的增强版。Callable接口提供了一个call()方法作为线程的执行体。call()方法比run()方法功能更强大。
    (a)call()方法可以有返回值
    (b)call()方法可以声明抛出异常

    但是,Callable接口不是Runnable接口的子接口,所以其实现类的实例并不能作为Thread的target。而且call()方法作为线程执行体,如何获取返回值呢?
    java5提供了一个Future接口来代表Callable接口call()方法的返回值,并为Future接口提供了一个实现类FutureTask,该实现类不仅实现了Future接口,还实现了Runnable接口,因此可以作为Thread的target。接口有几个公共的方法:
    (a)boolean cancle(boolean mayInterruptIfRunning):试图取消Future里关联的Callable任务
    (b)V get():call()方法返回值
    (c)V get(long timeout,TimeUnit unit):指定时间内call()没有返回值就抛出Timeoutxception
    (d)boolean isCanclled():任务正常完成前被取消则返回true
    (e)boolean isDone():任务已完成则返回true

    创建并启动有返回值线程的步骤:
    (a)创建Callable接口的实现类,并实现call()方法。在创建Callable接口的实现类(可以使用lambda表达式)
    (b)使用FutureTask类来包装Callable对象,该实现类封装了call()方法返回值
    (c)使用FutureTask对象作为Thread的target创建并启动新线程
    (d)调用FutureTask对象的get方法获取线程返回值

    示例(非Lambda表达式版):

    package czz;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    public class ThirdThread implements Callable<Integer> {
        @Override
        public Integer call(){
            int i=0;
            for (;i<100;i++){
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
            return i;
        }
    
    
        public static void main(String[] args) {
            ThirdThread callThread = new ThirdThread();
            FutureTask<Integer> task = new FutureTask<>(callThread);
            for (int i=0;i<500;i++){
                System.out.println(Thread.currentThread().getName() + "循环变量i= " + i);
                if (i==5){
                    new Thread(task,"有返回值的线程").start();
                }
            }
            try{
                System.out.println("子线程的返回值:"+task.get());
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    示例(Lambda表达式版):

    package czz;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    public class ThirdThread1 {
        public static void main(String[] args) {
            FutureTask<Integer> task = new FutureTask<>(()->{
                int i=0;
                for (;i<100;i++){
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
                return i;
            });
    
            for (int i=0;i<500;i++){
                System.out.println(Thread.currentThread().getName() + "循环变量i= " + i);
                if (i==5){
                    new Thread(task,"Lambda有返回值的线程").start();
                }
            }
            try{
                System.out.println("Lambda子线程的返回值:"+task.get());
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    三种方式的比较

    Runnable与Callable接口实现基本一致。这两种方式优缺点:
    (a)线程类还可以继承其他类
    (b)多个线程可以共享一个target,非常适合多个相同的线程处理同一份资源的情况。从而可以将CPU,代码,数据分开,形成清晰的模型,较好的体现了面向对象的编程思想。
    (c)缺点是编程复杂一些,如果需要访问当前线程,需要访问Thread.currentThread()

    采用Thread
    (a)缺点是不能继承其他父类
    (b)优点是编码简单,当前线程this即可。

    学无止境。大家可以关注我的微信公众号,方便利用零碎时间互相交流。共勉!

    路漫漫其修远兮,吾将上下而求索。。。

  • 相关阅读:
    无聊死了,救我
    人间奇事:什么都涨,就工资不涨
    关于Javascript Hijacking
    参考答案:关注成本和投资回报率
    IE中一个值得关注的JS问题
    100%高度的div(转载)
    维基百科开放拉
    乱弹:出名、中医、程序
    Javascript的for...in语句在IE和Firefox的差异
    真实案例:给所有想要创业的朋友的一个管理方面的测试题
  • 原文地址:https://www.cnblogs.com/caozz/p/thread01.html
Copyright © 2020-2023  润新知