zoukankan      html  css  js  c++  java
  • Java多线程的实现

        记得面试的时候,面试官问了Java多线程实现的方式有几种,它们之间的区别是什么?作为一个Java新手,将最近的学习总结如下:

        1、Java多线程实现方式

        Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用Callable和Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

      2、继承Thread类实现多线程

      通过继承Thread类来创建并启动多线程的步骤如下:

     (1)定义Thread的子类,重写该类的run()方法,该run()方法的方法体就代表线程需要完成的任务,也称线程执行体。

     (2)创建Thread子类的实例,即创建了线程对象。

     (3)调用线程对象的start()方法来启动该线程。注意:线程无返回值。

    public class ThreadTest extends Thread{
        private int i;
        public void run(){
            for(; i<100; i++){
                System.out.println(getName()+" " + i);
            }
        }
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            for(int i=0; i<100; i++){
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i == 20){
                    new ThreadTest().start();
                    new ThreadTest().start();
                }
            }
        }
    }
    View Code

      程序的运行结果如下:

            

      一般程序执行就main方法这一主线程,从main开始按顺序执行代码,当开启多线程时,将会有新的线程和main方法这个线程平行执行,也就是说执行顺序是随机的,有JVM管理。大家可以自己试试多执行几遍,会发现程序的结果是不同的。另外,从上面的结果我们可以看到,Thread-0和Thread-1两个线程输出的 i 变量不连续,说明Thread-0和Thread-1不能共享该实例属性。

      小结:使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

      3、实现Runnable接口实现多线程

      因为java不能多重继承,所以继承thread类后就不能继承别的类了,所以如果有一个类,它已继承了某个类,又想实现多线程,那就可以通过实现Runnable接口来实现。实现Runnable接口来创建并启动多线程步骤如下:

       (1)定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

     (2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象。该Thread对象才是真正的线程对象,只是该线程负责执行target的run()方法。

     (3)调用线程对象的start()方法来启动该线程。注意:线程无返回值。

    public class SecondThread implements Runnable{
        
        private int i;
        public void run(){
            for(; i<100; i++){
                System.out.println(Thread.currentThread().getName()+" " + i);
            }        
        }
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            for(int i=0; i<100; i++){
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i == 20){
                    SecondThread st = new SecondThread();
                    new Thread(st,"新线程1").start();
                    new Thread(st,"新线程2").start();
                }
            }
        }
    }
    View Code

     程序运行结果如下:

        

      上面结果可看出,两个子线程的 i 变量是连续的,也就是采用Runnable接口的方式创建的多个线程可以共享线程类的实例属性。因为程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,从而实现了资源的共享性。 

        4、使用Callable和Future实现多线程

        Callable接口提供一个call()方法作为线程执行体,但call()方法功能强大,可以有返回值,可以声明抛出异常。但由于Callable对象不能直接作为Thread的target,而且call()方法并不是直接调用,是作为线程执行体被调用的,所以Java提供Future接口来代表Callable接口里的call()方法,并提供一个FutureTask实现类来包装Callable对象,作为Thread的target。

        创建并启动有返回值的线程步骤如下:

       (1)创建Callable接口的实现类,并实现call()方法,作为线程执行体,且call()方法有返回值。

       (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象

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

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

    package com.Thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    public class ThirdThread implements Callable<Integer>{
    
        public Integer call()
        {
            int i = 0;
            for(; i < 100; i++)
                System.out.println(Thread.currentThread().getName()+" 的循环变量 i的值: "+ i);
            return i;
        }
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ThirdThread rt = new ThirdThread();
            FutureTask<Integer> task = new FutureTask<Integer>(rt);
            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();
            }
    
        }
    
    }
    View Code

        程序最后调用FutureTask对象的get()方法来返回call()方法的返回值,该方法将导致主线程被阻塞。运行上面的程序,将看到主线程和call()方法所代表的线程交替执行的情形,程序最后还会输出call()方法的返回值。

        5、创建线程的三种方式对比

        以上三种方式都能实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常。

        ——采用实现Runnable、Callable接口的方式创建多线程的特点如下:

     (1)线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

     (2)在这种方式下,多个线程可以共享一个target对象,实现资源的共享。

     (3)劣势是:编程稍复杂,如需访问当前线程,则必须使用Thread.currentThread()方法。

      ——采用继承Thread类的方式创建多线程的特点:

     (1)优势:编写简单,如需访问当前线程,直接使用this即可获得当前线程。

     (2)劣势:因为线程类已经继承了Thread类,java不支持多继承,所以不能再继承其他父类。

    
    
  • 相关阅读:
    用 ArcMap 发布 ArcGIS Server FeatureServer Feature Access 服务 PostgreSQL 版本
    ArcMap 发布 ArcGIS Server OGC(WMSServer,MapServer)服务
    ArcScene 创建三维模型数据
    ArcMap 导入自定义样式Symbols
    ArcMap 导入 CGCS2000 线段数据
    ArcMap 导入 CGCS2000 点坐标数据
    ArcGis Server manager 忘记用户名和密码
    The view or its master was not found or no view engine supports the searched locations
    python小记(3)操作文件
    pytest(2) pytest与unittest的区别
  • 原文地址:https://www.cnblogs.com/chenbjin/p/3635498.html
Copyright © 2011-2022 走看看