多线程的三种建立方式

原创 2017年09月13日 18:53:07

继承Thread类创建线程类

public class Thread extends Object implements Runnable

  1. 定义Thread类的子类,并重写其run()方法
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法启动线程
public class FirstThread extends Thread {
    public void run(){
        for(int i=0;i<100;i++){
            /*
             * Thread类已经继承了Object
             * Object类创建了name选项 并且有其getName(),setName()方法
             * 在继承Thread的类里面使用时只需要用this引用
            */
            System.out.println(this.getName()+" "+i);
        }
    }

    public static void main(String[] args) {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20){
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }

}

Thread类已经继承了Object
Object类创建了name选项 并且有其getName(),setName()方法
在继承Thread的类里面使用时只需要用this引用

上面两个副线程和主线程随机切换,又因为使用的是继承Thread的类所以两个副线程不能共享资源

start()方法调用后并不是立即执行多线程代码,而是使得该线程编程可运行状态,什么时候运行是由操作系统决定的


实现Runnable接口创建线程类

public Thread()
public Thread(Runnable target)
public Thread(Runnable target,String name)

  1. 定义Runnable接口的实现类,并重写该接口的run()方法
  2. 创建Runnable实现类的实例,并以此作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
public class SecondThread implements Runnable {
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }

    public static void main(String[] args) {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);

            if(i==20){
                SecondThread st=new SecondThread();
                //通过new Thread(target,name)创建线程
                new Thread(st,"新线程1").start();
                new Thread(st,"新线程2").start();
            }
        }
    }
}

上面的结果是两个副线程和主线程随机切换,但是并没有共享资源,因为他们根本没有能用来共享的资源。

start()方法调用后并不是立即执行多线程代码,而是使得该线程编程可运行状态,什么时候运行是由操作系统决定的


继承Thread类和创建Runnable接口的共享资源详解

在只有可以用来共享的资源时候,也就是同用一个实例化对象。两个创建方式在共享资源时才会有所区别,否则它们都不会共享资源共享资源通常用private static 修饰符来修饰。

class Thread1 extends Thread{  
    private int count=5;  
    private String name;  
    public Thread1(String name) {  
       this.name=name;  
    }  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            System.out.println(name + "运行  count= " + count--);  
            try {  
                sleep((int) Math.random() * 10);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  

    }  
}  

public class Main {  

    public static void main(String[] args) {  
        Thread1 mTh1=new Thread1("A");  
        Thread1 mTh2=new Thread1("B");  
        mTh1.start();  
        mTh2.start();  

    }  

}  

B运行 count= 5
A运行 count= 5
B运行 count= 4
B运行 count= 3
B运行 count= 2
B运行 count= 1
A运行 count= 4
A运行 count= 3
A运行 count= 2
A运行 count= 1

正是因为有了private int count=5;一句才有了共享资源,但这是继承Thread类的子类,并不能共享资源

class Thread2 implements Runnable{  
    private int count=15;  
    public void run() {  
          for (int i = 0; i < 5; i++) {  
              System.out.println(Thread.currentThread().getName() + "运行  count= " + count--);  
                try {  
                    Thread.sleep((int) Math.random() * 10);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  

    }  

}  
public class Main {  

    public static void main(String[] args) {  

        Thread2 my = new Thread2();  
            new Thread(my, "C").start();//同一个mt,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常     
            new Thread(my, "D").start();  
            new Thread(my, "E").start();  
    }  

}  

C运行 count= 15
D运行 count= 14
E运行 count= 13
D运行 count= 12
D运行 count= 10
D运行 count= 9
D运行 count= 8
C运行 count= 11
E运行 count= 12
C运行 count= 7
E运行 count= 6
C运行 count= 5
E运行 count= 4
C运行 count= 3
E运行 count= 2

同样的正是因为有了private int count=15这个共同的实例化对象,实现Runnable的类才可以共享资源

那么为什么继承Thread类的子类实现Runable接口的类在共享资源时有区别呢?
因为Java中只能支持单继承,单继承特点意味着只能有一个子类去继承
而Runnabl接口后可以跟好多类,便可以进行多个线程共享一个资源的操作


使用Callable和Future创建线程

Callable怎么看起来都像Runnable接口的增强版,Callable有一个call()方法相当于Runnable的run()方法,但是功能却更加强大:

call()方法可以有返回值
call()方法可以声明抛出异常

Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法的返回值类型相同。
而且Callable接口是函数式接口,因此可使用Lambda表达式创建Callable对象
Runnable接口也是函数式接口,因此也可以使用Lambda表达式创建Runnable对象

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,再创建Callable实现类的实例
  2. 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法
  3. 使用FutureTask类对象作为Thread对象的target创建并启动新线程
  4. 调用FutureTask对象的get()方法来获得子线程结束后的返回值
public class ThirdThread implements Callable<Integer> {
    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 tt=new ThirdThread();
        FutureTask<Integer> task=new FutureTask<>(tt);
        Thread t=new Thread(task,"有返回值的线程");
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20){
                t.start();
            }
        }
        try{
            System.out.println("返回值是:"+task.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

使用Lambda表达式的Callable和Future创建的线程

public class ThirdThread{
    public static void main(String[] args){
        ThirdThread tt=new ThirdThread();
        //先使用Lambda表达式创建Callable<Integer>对象
        //使用FutureTask封装Callable对象
        FutureTask<Integer> task=new FutureTask<Integer>((Callable<Integer>)()->{
            int i=0;
            for(;i<100;i++){
                System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i);
            }
            return i;
        });

        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i);
            if(i==20){
                new Thread(task,"有返回值的线程").start();
            }
        }
        try{
            System.out.println("子线程的返回值"+task.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

创建线程的三种方法

第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务。 第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务。 第三种方法:实...
  • baidu_21578557
  • baidu_21578557
  • 2016年04月16日 21:34
  • 5316

三种线程同步方式

三种线程同步方式,即
  • Sharing_Li
  • Sharing_Li
  • 2014年08月30日 20:50
  • 1484

JAVA多线程实现的三种方式

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没...
  • aboy123
  • aboy123
  • 2014年07月31日 18:34
  • 449911

Java中线程的三种实现方式

Java中使用Thread代表线程类,所有的线程类都必须是Thread类或者其子类的实例。每个线程的作用就是完成一定的任务,即执行一段程序流。Java中使用线程执行体来表示这段程序流。 Java中...
  • FFFLLLLLL
  • FFFLLLLLL
  • 2016年08月21日 22:39
  • 1804

创建线程的三种方式

一、继承Thread类+重写run()方法 启动:创建子类对象+对象.start() 二、实现Runnable接口+重写run方法 启动:使用静态代理 1)、创建真实角色 2)、创建代理角色...
  • scgaliguodong123_
  • scgaliguodong123_
  • 2015年05月27日 17:13
  • 5974

【多线程】三种方案实现多线程之间相互协作的通信

在并发编程中,经常会遇到多个线程之间需要相互协作的情况,即并不是多个线程同时执行,而是按照一定的顺序循环执行的情况。 那么怎样去实现这种效果呢?这里介绍三种方案。 这里都以子线程循环10次,然后主线程...
  • wangyy130
  • wangyy130
  • 2016年07月27日 14:37
  • 2152

java创建线程的三种方式及其对比

Java中创建线程主要有三种方式: 一、继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行...
  • longshengguoji
  • longshengguoji
  • 2014年11月14日 22:14
  • 89484

JAVA多线程实现的三种方式

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没...
  • mccand1234
  • mccand1234
  • 2016年07月20日 21:56
  • 4759

有两种实现多线程的方式以及两种方式之间的区别

Java中有两种实现多线程的方式。一是直接继承Thread类,二是实现Runnable接口。那么这两种实现多线程的方式在应用上有什么区别呢?          为了回答这个问题,我们可以通过编写...
  • u013449800
  • u013449800
  • 2014年08月22日 18:40
  • 3937

两个栈实现一个队列的三种方式思路

已知下面Stack类及其3个方法Push、Pop和 Count,请用2个Stack实现Queue类的入队(Enqueue)出队(Dequeue)方法。   class Stack { … p...
  • summer070825
  • summer070825
  • 2016年09月28日 17:08
  • 205
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:多线程的三种建立方式
举报原因:
原因补充:

(最多只允许输入30个字)