本文详细介绍了并发相关概念、为什么要用多线程、java中多线程的实现方式、线程状态及状态间转换等内容。

一、概念

程序:程序(program)是存放在磁盘文件中的可执行文件

进程:程序的执行实例被称为进程(process),进程是系统资源分配的最小单元。一个进程至少包括一个线程,所有的线程共享这个进程的资源。多进程指操作系统同时运行多个程序。如操作系统中同时运行着QQ、微信等程序

线程:线程是CPU调度的最小单元,线程被包含在进程中。多线程指的是统一进程中同时运行多个线程,如迅雷运行时,同时进行多个文件的下载。同一进程中,各个线程拥有独立的运行栈和程序计数器。

并行:多个CPU示例或者多台机器同时执行一段处理逻辑,是真正的同时

并发:并发是指多个任务在同一时间段类发生。通过CPU调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反映这个系统的处理能力

线程安全:指在并发的情况下,线程的调度顺序不影响运行结果

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。

为什么要使用多线程,简单来说有两个原因:

  • 更好的利用cpu资源
  • 更好的编程模型

二、线程创建方式

java提供了三种方式实现线程。

2.1 实现Runnable接口

Runable定义

package java.lang;

/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   JDK1.0
 */
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

总结:

  • 如果希望一个类的实例被当做一个线程执行,那么这个类必须实现Runnable接口。
  • Runnable接口被设计成线程设计的一种公共协议。
  • Thread类就是一种Runnable接口的实现。
  • 当一个实现了Runnable接口的线程类开始运行,就会自动调用run()方法。
  • 实现Runnable接口,必须重写run()方法。
  • 建议在run()方法中存放所有的业务代码,做到线程控制与业务流程的分离。
  • run()方法返回类型为Void,参数为空。
  • run()方法不能抛出异常。

使用语法:

//定义
public class MyRunnableImpl implements Runnable {
    @Override
    public void run(){
        //..
    }
}

//使用
new Thread(new MyRunnableImpl()).start();

2.2 继承Thread类

Thread线程类实现了Runnable接口,并拓展了一系列线程方法。

使用语法:

//定义
public class MyThread extends Thread {
    @Override
    public void run(){
        //..
    }
}

//使用
new MyThread().start();

Runnable和Thread相比优点有:
(1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以。

(2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,没有必要(下面的代码展示两种创建线程的方法都可以实现资源共享)。我们从Thread源码中也可以看到,当以Thread方式去实现资源共享时,实际上源码内部是将thread向下转型为了Runnable,实际上内部依然是以Runnable形式去实现的资源共享。

public class  Test {

    public static void main(String[] args) {
        A a = new A();
        new Thread(a).start();
        new Thread(a).start();

        B b = new B();
        new Thread(b).start();
        new Thread(b).start();
    }

}

class A implements Runnable {
    //共享资源
    private AtomicInteger a = new AtomicInteger(0);

    public void run() {
        System.out.println(a.incrementAndGet());
    }
}

class B extends Thread{
    //共享资源
    private AtomicInteger b = new AtomicInteger(0);

    @Override
    public void run() {
        b.incrementAndGet();
    }

}

    /**
     * Thread构造器
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

2.3 实现Callable接口

先来分析之前两种实现方式的弊端。
通过分析Runnable接口的定义,很容易总结出来:

  • 没有返回值:如果想要获取某个执行结果,需要通过共享变量等方式,需要做更多的处理。
  • 无法抛出异常:不能声明式的抛出异常,增加了某些情况下的程序开发复杂度。
  • 无法手动取消线程:只能等待线程执行完毕或达到某种结束条件,无法直接取消线程任务。

为了解决以上的问题,在JDK5版本的java.util.concurrent包中,引入了新的线程实现机制:Callable接口

Callable接口定义

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code call}
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

总结:

  • Callable接口更倾向于针对任务这个概念。一个任务其实就是一个线程
  • Callable接口实现的任务必须返回一个结果,当无法正常返回结果时抛出一个异常
  • Callable接口的实现类需要重写一个无参的方法Call()
  • Callable接口Runnable接口类似,都是为了实现多线程而设计的。
  • Runnable接口没有返回值,也无法抛出异常。
  • Callable接口是一个泛型接口。

使用语法:

//定义
public class MyCallableImpl implements Callable<T> {
    @Override
    public T call() throws Exception {
        //...
    }
}

//使用
//一般配置Executor使用,Executor提供线程池服务
ExecutorService executor = new ....
//一般配置Future接口使用,Future用于保存返回结果
//向线程池提交一个任务,并把此任务的执行情况保存在future中
Futrue future = executor.submit(new MyCallableImple());
//获取返回结果
future.get();
//关闭线程池和任务
executor.shutdwon();

说明:

  • Future、Callable一般与Executor结合使用。
  • Callable接口用于定义任务类,并在Call()方法中定义业务代码。
  • Executor接口负责线程池的管理(计划在后续章节进行学习)。
  • Future接口负责保持在Executor线程池中运行的Callable任务的运行状态。
  • Callable接口实现类,通过executor.submit()向线程池提交任务。
  • Future接口通过get()方法获取执行结果(一直等待知道结果产生)。
  • 一定要记得通过executor.shutdwon()关闭线程池。推荐在finally中进行这个操作。

实例场景:

定义一个最大线程数为5的线程池,运行5个任务,获取并打印出每个线程的执行时间。

实例代码:

/**
 * <p>自定义线程03:实现Callable接口</p>
 *
 * @author hanchao 2018/3/12 8:56
 **/
//注意,Callable是一个泛型接口
public class MyCallableImpl implements Callable<Integer> {

    /**
     * <p>实现Callable需要重写call方法,此方法有返回值</p>
     *
     * @author hanchao 2018/3/12 8:59
     **/
    @Override
    public Integer call() throws Exception {
        Integer interval = new Random().nextInt(1000);
        Thread.sleep(interval);
        return interval;
    }

    /**
     * <p>实现Callable示例</p>
     *
     * @author hanchao 2018/3/12 9:00
     **/
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        //Future、Callable一般与Executor结合使用
        //Executor负责创建线程池服务
        //实现Callable接口形成的线程类,负责处理业务逻辑,并将处理结果返回
        //Future接口负责接收Callable接口返回的值
        ExecutorService executor = Executors.newFixedThreadPool(5);
        try {
            //定义一组返回值
            Future<Integer>[] futures = new Future[5];
            //向线程池提交任务
            for (int i = 0; i < 5; i++) {
                //注意Future的参数化类型要与Callable的参数化类型一致
                futures[i] = executor.submit(new MyCallableImpl());
            }
            //输出执行结果
            for (int i = 0; i < 5; i++) {
                System.out.println(futures[i].get());
            }
        }finally {//将关闭线程池放在finally中,最大限度保证线程安全
            //记得关闭这个线程池
            executor.shutdown();
        }
    }
}

更多的Future方法

前面只展示了Future类的get()方法。
为了更详细的体现[实现Callable接口]这种方式的优点,对Future接口的其他方法进行简单学习。

Future接口的主要方法如下:

  • isDone():判断任务是否完成。
  • isCancelled():判断任务是否取消。
  • get():获取计算结果(一致等待,直至得到结果)。
  • cancel(true):取消任务。
  • get(long,TimeUnit):规定时间内获取计算结果(在long时间内等待结果,如果得到则返回;如果未得到,则结束,并抛出TimeoutException异常)。

说明:

  • get(long,TimeUnit)的第一个参数是最大超时时间,第二个是时间单位,可以通过enum TimeUnit获取。

三、线程状态及状态间转换

涉及到状态转换的方法有(以下方法将在后续章节详细学习):

  • Thread.sleep(long):强制线程睡眠一段时间。
  • thread.start():启动一个线程。
  • thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。
  • thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。
  • thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。
  • object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

3.1 Java线程的状态

关于Java线程的状态,都规定在了Thread.State这个枚举类型中,我们先看看Thread.State的源码:

    /**
     * A thread state.  A thread can be in one of the following states:
     * <ul>
     * <li>{@link #NEW}<br>
     *     A thread that has not yet started is in this state.
     *     </li>
     * <li>{@link #RUNNABLE}<br>
     *     A thread executing in the Java virtual machine is in this state.
     *     </li>
     * <li>{@link #BLOCKED}<br>
     *     A thread that is blocked waiting for a monitor lock
     *     is in this state.
     *     </li>
     * <li>{@link #WAITING}<br>
     *     A thread that is waiting indefinitely for another thread to
     *     perform a particular action is in this state.
     *     </li>
     * <li>{@link #TIMED_WAITING}<br>
     *     A thread that is waiting for another thread to perform an action
     *     for up to a specified waiting time is in this state.
     *     </li>
     * <li>{@link #TERMINATED}<br>
     *     A thread that has exited is in this state.
     *     </li>
     * </ul>
     *
     * <p>
     * A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     *
     * @since   1.5
     * @see #getState
     */
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

从源代码,总结出Java线程的状态有以下几种:

  • NEW:一个尚未启动的线程的状态。也称之为初始状态、开始状态
  • RUNNABLE:一个可以运行的线程的状态,可以运行是指这个线程已经在JVM中运行了,但是有可能正在等待其他的系统资源。也称之为就绪状态、可运行状态
  • BLOCKED:一个线程因为等待监视锁而被阻塞的状态。也称之为阻塞状态
  • WAITING:一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有三种,分别是调用Object.wait()、join()以及LockSupport.park()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
  • TIMED_WAITING:一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有五种,分别是:Thread.sleep(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUntil(obj,long)。
  • TERMINATED:一个完全运行完成的线程的状态。也称之为终止状态、结束状态

3.2 线程状态转换

java并发01-细说多线程

参考资料

https://www.cnblogs.com/wxd0108/p/5479442.html

https://img2018.cnblogs.com/blog/728328/201907/728328-20190716234830553-1887496190.png

https://blog.csdn.net/hanchao5272/article/details/79437370