zoukankan      html  css  js  c++  java
  • 线程创建与启动

    什么是线程?

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

    线程的优势

    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即可。

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

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

  • 相关阅读:
    Bootstrap 2.2.2 的新特性
    Apache POI 3.9 发布,性能显著提升
    SQL Relay 0.48 发布,数据库中继器
    ProjectForge 4.2.0 发布,项目管理系统
    红帽企业 Linux 发布 6.4 Beta 版本
    红薯 快速的 MySQL 本地和远程密码破解
    MariaDB 宣布成立基金会
    Percona XtraBackup 2.0.4 发布
    Rocks 6.1 发布,光盘机群解决方案
    精通Servlet研究,HttpServlet的实现追究
  • 原文地址:https://www.cnblogs.com/caozz/p/thread01.html
Copyright © 2011-2022 走看看