Java多线程——概述

什么是线程?

线程是指程序在执行过程中,能够执行程序代码的一个执行单元。在Java语言中,线程有4种状态:运行、就绪、挂起和结束。

进程是指一段正在执行的程序。而线程有时也被称为轻量级进程,它是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间。
Java多线程——概述

为什么使用线程?

在操作系统级别上,程序的执行都是以进程为单位的,而每个进程中通常都会有多个线程互不影响的并发执行,那么为什么要使用多线程呢?其实,多线程的使用为程序研发带来巨大的便利,具体而言,有以下几个方面的内容:
1) 使用多线程可以减少程序的响应时间。在单线程(单线程指的是程序执行过程中只有一个有效操作的序列,不同操作之间都有明确的执行先后顺序)的情况下,如果某个操作很耗时,或者陷入长时间的等待(如等待网络响应),此时程序将不会响应鼠标和键盘的操作,使用多线程后,可以把这个耗时的线程分配到一个单独的线程去执行,从而使程序具备了更好的交互性。
2) 与进程相比,线程的创建和切换开销更小。由于启动一个新的进程必须给这个进程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一进程内的线程共享代码段、数据段、线程的启动或切换的开销要比进程要少很多。同时多线程在数据共享方面效率非常高。
3) 多CPU或多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算机资源,造成资源的巨大浪费。因此在多CPU计算机上使用多线程能提高CPU的利用率。
4) 使用多线程能简化程序的结构,使程序便于理解和维护。一个非常复杂的进程可以分成多个线程来执行。

如何实现Java多线程?

一般有3种方法,其中前两种为最常用的方法。

  1. 继承Thread类,重写run方法
    Thread类本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()方法。start()方法是一个native(本地)方法,它将启动一个新线程,并执行run()方法(Thread中提供的run()方法是一个空方法)。

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

  1. 实现Runnable接口,并实现run()方法
    (1) 自定义类并实现Runnable接口,实现run()方法。
    (2) 创建Thread对象,用实现Runnable接口的对象作为参数,实例化该Thread对象。
    (3) 调用Thread的start()方法。

其实,不管通过集成Thread类还是通过使用Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。

  1. 实现Callable接口,重写call()方法
    Callable接口实际上是Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能,主要表现为以下3点:
    1) Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能。
    2) Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常。
    3) 运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值。在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,当调用Future的get()方法以获取结果时,当前线程就会阻塞,直到call()方法结束并返回结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test{

    //创建线程类,运行完后会返回一个字符串String
    public static class CallableTest implements Callable<String>{
        public String call() throws Exception{
            Thread.sleep(3000);
            return "线程运行结束";
        }
    }

    public static void main(String[] args){
        //创建线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //向线程池中提交线程
        Future<String> future = threadPool.submit(new CallableTest());
        try {
            System.out.println("等待线程运行结束...");
            System.out.println(future.get());
        } catch (Exception e) {
        }
    }
}

多线程同步的实现方法有哪些?

当使用多线程访问同一个资源时,非常容易出现线程安全的问题。因此,需要采用同步机制来解决这种问题。Java主要提供了3种实现同步机制的方法:
(1)synchronized关键字
1) synchronized方法。在方法的声明前加入synchronized关键字
2) synchronized块

(2)wait()方法与notify()方法

(3)Lock
JDK 5新增加了Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步。