多线程

  一、线程概述

    1.进程和线程的区别

      进程:进程是处于运行过程中的程序,并且具有一定的功能,是系统进行资源分配和调度的一个独立单位。

         主要特征:

        (1)独立性:系统中独立存在的实体,拥有自己的独立资源,拥有自己的私有地址空间,一个用户进程在不经过进程本身允许的情况下,

      不能访问其他进程的地址空间。

        (2)动态性:体现在进程和程序的区别。程序的静态的指令集合,而进程则是动态的指令集合,添加了时间。

        (3)并发性:多个进程在单个CUP上并发执行,多个进程之间不会相互影响。

            补充:并发:在同一时刻执行一条指令,但是多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果。

               并行:在同一时刻,有多条指令在多个处理器上同时执行。

      线程:进程的执行单位,在进程中独立的,并发的执行流,拥有自己的堆栈,自己的程序计数器和自己的局部变量,不拥有资源,与父进程的线程

      共享该进程的全部资源。

          主要特征:独立运行,抢占式执行。

      总结:操作系统可以同时执行多个任务,每个任务就是进程,进行也可以同时执行多个任务,每个任务就是线程。

    2.多线程的优势

      (1)进城之间不能共享内存,但是线程之间内存共享非常容易。

      (2)系统创建进程需要为该进程重新分配系统资源,但是创建线程代价小,因此开发效率较高。

      (3)Java语言内置了多线程的功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化Java的多线程编程。

  二、线程的创建和启动

    1.创建线程的两三种方法

      (1)继承Thread类创建线程类

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

          a.定义Thread的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此run()方法称为线程执行体。

          b.创建Thread子类的实例,即创建了线程对象。

          c.调用线程对象的start()方法来启动线程。

        实例如下:

          

/**
 * 
 * @author fengkuirui
 * @date 2017-02-09
 * 通过继承Thread类创建线程
 */
public class FirstThread extends Thread {
    private int i;
    //重写run()方法
    @Override
    public void run(){
        for(;i<100;i++){
            //当线程继承Thread类时,直接实用this即可获得当前线程
            //Thread类的getName()返回当前线程的名字
            System.out.println(this.getName()+" "+i);
        }
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++){
            //调用Thread的currentThread()方法获取当前线程。
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i == 20){
                //创建并启动第一个线程
                new FirstThread().start();
                //创建启动第二个线程
                new FirstThread().start();
            }
        }
    }
}

        运行结果如下:

          

        注意:通过继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

      (2)实现Runnable接口创建线程类

        (1)步骤

          a.定义Runnable接口的实现类,并重写该接口的run()方法。

          b.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

          c.调用start()方法启动该线程。

         (2)实例如下:

          

/**
 * 
 * @author fengkuirui
 * @date 2017-02-09
 * 通过实现Runnable接口创建线程类
 */
public class SecondThread implements Runnable{
    private int i;
    @Override
    public void run(){
        for(;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();
            }
        }
    }
}

        (3)运行结果如下:

           

        注意:采用Runnable接口的方式创建的多个线程是可以共享线程类的实例变量。

      3.使用Callable和热Future创建线程

        (1)步骤

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

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

          c.使用FutureTask对象作为Thread对象的target创建并启动线程。

          d.调用FutureTask对象的get()方法来实现线程类,并启动该线程。

        (2)实例如下:    

          

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 
 * @author fengkuirui
 * @date 2017-02-09
 * 使用Callable和Future创建线程
 */
public class ThridThread implements Callable<Integer>{
    
    

    @Override
    public Integer call() throws Exception {
        // TODO Auto-generated method stub
        int i=0;
        for(;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        //call()可以有返回值;
        return i;
    }
    public static void main(String[] args) {
        //创建Callable对象
        ThridThread rt = new ThridThread();
        //使用FutureTask来包装Callable对象
        FutureTask<Integer> task = new FutureTask<>(rt);
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i == 20){
                //实质还是以Callable对象创建并启动线程;
                new Thread(task,"有返回值的线程").start();
            }
        }
        try{
            //获取线程返回值
            System.out.println("子线程返回值: "+task.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

        运行结果如下:

          

          

    2.三种创建线程方式的区别

      (1)采用实现Runnable接口、Callable接口的方式创建线程的优缺点:

        a.线程类还可以继续继承其他类

        b.这种方式多线程共享一个target对象,所以非常适合多个相同的线程来处理同一份资源情况下,采用此种方式。

        缺点:编程稍微复杂,需要访问当前线程时,必须使用Thread.currentThread()方法。

      (2)采用继承Thread类来创建线程的优缺点:

        优点:编写简单。

        缺点:不能继承其他父类。

  

原文地址:https://www.cnblogs.com/fkrcode/p/6382663.html