zoukankan      html  css  js  c++  java
  • java多线程——线程的创建

    java多线程——线程的创建

    1、创建线程方式一:继承Thread类

    (1)定义一个类继承Thread;

    (2)重写run方法;

    (3)创建线程对象;

    (4)调用start方法,开启线程并让线程执行,同时还会告诉JVM调用run方法。

     1 class Demo extends Thread{
     2     private String name;
     3     Demo(String name){
     4         this.name=name;
     5     }
     6     @Override
     7     public void run() {
     8         for(int i=1;i<=20;i++) {
     9             System.out.println("name="+name+"......."+i);
    10         }
    11     }
    12 }
    13 
    14 public class ThreadDemo {
    15     public static void main(String[] args) {
    16         //method0();
    17         //method1();
    18         Demo d1=new Demo("小强");
    19         Demo d2=new Demo("旺财");
    20         d2.start();//开启多一个执行路径
    21         d1.run();//由主线程负责
    22     }
    23 
    24     public static void method1() {
    25         Demo d1=new Demo("小强");
    26         Demo d2=new Demo("旺财");
    27         d1.run();//由主线程负责
    28         d2.start();//开启多一个执行路径,主线程结束才开启
    29     }
    30 
    31     public static void method0() {
    32         Demo d1=new Demo("小强");
    33         Demo d2=new Demo("旺财");
    34         d1.run();//由主线程负责
    35         d2.run();//由主线程负责
    36     }        
    37 }
    ThreadDemo

    以上代码中,method1和method0运行效果相同,但是method1确实开启了多线程,只是在主线程结束后才开启。

    线程对象调用run方法和调用start方法的区别?

    调用run方法不开启线程,仅是对象调用方法;调用start开启线程,并让JVM调用run方法在开启的线程中执行。

    为什么要继承Thread类并重写run方法?

    因为Thread类描述的是线程事物,具备线程该有的功能。既然如此?为什么不能直接创建Thread类的对象:Thread t1=new Thread(); t1.start();这种写法没有错,但是该start方法调用的是Thread类中的run方法,而这个run方法没有任何操作,更重要的是,这个run方法中没有定义我们需要让线程执行的代码。创建线程的目的是为了建立单独的路径,让多部分代码实现同时执行。也就是说,线程创建并执行需要给定的代码(线程的任务)。对于我们常用的主线程,它的任务都定义在main函数中。自定义线程需要执行的任务都定义在run方法中。Thread类中的run方法内部的任务不是我们所需要,所以需要进行run方法的重写。

    内存占用:多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间,进行方法的压栈和弹栈。当执行线程的任务结束了,线程自动在栈内存中释放了。当所有的执行线程都结束了,进程就结束了。

    2、创建线程方式二:实现Runnable接口

    (1)声明一个类实现Runnable接口(避免了继承Thread类的单继承局限性)。

    (2)该类实现run方法(将线程任务代码定义到run方法中)。

    (3)创建Thread类的对象(只有创建Thread类的对象才能创建线程)。

    (4)将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

    (5)启动线程。

     1 //)声明一个类实现Runnable接口,该类实现run方法。
     2 class Demo2 implements Runnable{
     3     private String name;
     4     Demo2(String name){
     5         this.name=name;
     6     }
     7     @Override
     8     public void run() {
     9         for(int i=1;i<=20;i++) {
    10             System.out.println("name="+name+"....."+Thread.currentThread().getName()+"...."+i);
    11         }
    12     }
    13 }
    14 
    15 public class ThreadDemo2 {
    16     public static void main(String[] args) {
    17         //(2)创建Runnable子类的对象
    18         Demo2 d1=new Demo2("小强");
    19         Demo2 d2=new Demo2("旺财");
    20         //(3)创建Thread类的对象,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
    21         Thread t1=new Thread(d1);
    22         Thread t2=new Thread(d2);
    23         //(4)启动线程。
    24         t1.start();
    25         t2.start();
    26         for(int i=0;i<20;i++) {
    27             System.out.println(Thread.currentThread().getName()+"----->"+i);
    28         }
    29         
    30     }
    31 }
    ThreadDemo2

    在Thread类中相关源码抽离出来如下:

     1 public class Thread {
     2     private Runnable target;
     3     public Thread(Runnable target){
     4         this.target=target;
     5     }
     6     
     7     public void run() {
     8         if (target != null) {
     9             target.run();
    10         }
    11     }    
    12 }

    优点(避免单继承+解耦):

    第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。

    实现Runnable接口的方式,更加符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既有线程对象,又有线程任务。而Runnable接口,将线程任务单独分离出来,封装成对象,类型就是Runnable接口类型。

    3、创建线程方式三:使用Callable和Future创建线程

    Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大call()方法可以有返回值,且call()方法可以声明抛出异常。

    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法没有返回值,再创建Callable实现类的实例。(从java8开始,可以直接使用Lambda表达式创建Callable对象)。

    (2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

    (3)使用FutureTask作为Thread对象的target创建并启动新线程。

    (4)调用FutureTask对象的get方法来获得子线程执行结束后的返回值。

     1 import java.util.concurrent.Callable;
     2 import java.util.concurrent.ExecutionException;
     3 import java.util.concurrent.FutureTask;
     4 
     5 class DemoCall implements Callable{
     6     private int n;
     7     @Override
     8     public Object call() throws Exception {
     9         int i=1;
    10         while(i++<n) {
    11             System.out.println(Thread.currentThread().getName()+"-------->"+i);
    12         }
    13         return Thread.currentThread().getName()+"运行完毕----------n="+n;
    14     }
    15     
    16     public DemoCall(int n) {
    17         this.n=n;
    18     }
    19 }
    20 class CallableDemo {
    21     public static void main(String[] args) {
    22         DemoCall d1=new DemoCall(20);
    23         DemoCall d2=new DemoCall(50);
    24         // 使用Callable方式创建线程,需要FutureTask类的支持,用于接收运算结果,可以使用泛型指定返回值的类型
    25         FutureTask<String> ft1=new FutureTask<String>(d1);
    26         FutureTask<String> ft2=new FutureTask<String>(d2);
    27         new Thread(ft1).start();
    28         new Thread(ft2).start();
    29          // 接收运算结果
    30         // 只有当该线程执行完毕后才会获取到运算结果,等同于闭锁的效果
    31         try {
    32             System.out.println(ft1.get());
    33             System.out.println(ft2.get());
    34         } catch (InterruptedException e) {
    35             // TODO Auto-generated catch block
    36             e.printStackTrace();
    37         } catch (ExecutionException e) {
    38             // TODO Auto-generated catch block
    39             e.printStackTrace();
    40         }
    41         
    42     }
    43 }
    CallableDemo
  • 相关阅读:
    图像的纹理分析
    图像的小波变换
    图像的哈尔变换
    图像的K-L变换
    图像的斜变换
    图像的波尔什-哈达玛变换
    今日心得:给自己写信
    今日心得:人的幸福感取决于什么?
    今日心得:人生就像一杯茶,不会苦一辈子但会苦一阵子
    今日心得:纪念徐志摩117周年
  • 原文地址:https://www.cnblogs.com/hopeyes/p/9739511.html
Copyright © 2011-2022 走看看