java多线程详解(二)之创建线程的方式

一、Linux中线程的状态

  Linux中线程状态一共有5种:

  • 初始状态(New):对应 Java中的 NEW 状态
  • 可运行状态(Ready):对应 Java中的 RUNNBALE 状态
  • 运行状态(Running):对应 Java中的 RUNNBALE 状态
  • 等待状态(Waiting):该状态在 Java中被划分为了 BLOCKEDWAITINGTIMED_WAITING 三种状态
  • 终止状态 (Terminated):对应 Java中的 TERMINATED 状态

二、java中线程的状态

  Java中线程的状态一共有6种(生命周期)对应Java Thread类种State枚举:

  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;
    }

  • New(新创建):新创建了一个线程对象,但还没有调用Thread的start()方法
  • Runnable(可运行):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其它线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)
  • Blocked(被阻塞):从箭头的流转方向可以看出,从 Runnable 状态进入 Blocked 状态只有一种可能,就是进入 synchronized 保护的代码时没有抢到 monitor 锁,无论是进入 synchronized 代码块,还是 synchronized 方法,都是一样。当处于 Blocked 的线程抢到 monitor 锁,就会从 Blocked 状态回到Runnable 状态。
  • Waiting(等待):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
  • Timed Waiting(计时等待):该状态不同于WAITING,它可以在指定的时间后自行返回
  • Terminated(被终止):表示该线程已经执行完毕。线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
  • 如果想要确定线程当前的状态,可以通过 getState() 方法,并且线程在任何时刻只可能处于 1 种状态。

三、java线的实现方式

3.1、从编程的角度上来说,在java中线程的实现方式有三种

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口

但是实质上都是 new Thread(…).start(); 最终还是由Thread类来实现线程。

3.2、继承自Thread类,步骤如下

  1. 创建 MyThread 类,让其继承 Thread 类并重写 run() 方法。
  2. 创建 MyThread 类的实例对象,即创建一个新线程。
  3. 调用 start() 方法,启动线程。

代码示例:

public class MyThread  extends Thread{

    @Override
    public void run() {
        System.out.println(" 继承自 Thread 类 创建的线程,ThreadName:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {

        MyThread myThread1 = new MyThread();
        myThread1.setName("Thread-1");
        System.out.println(myThread1.getState());
        myThread1.start();


        MyThread myThread2 = new MyThread();
        myThread2.setName("Thread-2");
        System.out.println(myThread2.getState());
        myThread2.start();


        MyThread myThread3 = new MyThread();
        myThread3.setName("Thread-3");
        System.out.println(myThread3.getState());
        myThread3.start();

        System.out.println(myThread1.getName()+" state:"+myThread1.getState());
        System.out.println(myThread2.getName()+" state:"+myThread2.getState());
        System.out.println(myThread3.getName()+" state:"+myThread3.getState());

   

    }
}

运行结果:

 从结果上可以看到,线程执行的顺序是随机的,只要不改变最终的结果,执行的顺序可以进行调整的。

使用了Thread类中的方法

        1、set/getName()  为新线程设置名字。

        2、getState() 获取当前线程的运行状态。

        3、start() 开启线程运行。

3.3 实现 Runnable() 接口

步骤如下:

  1. 定义一个类RunableTask实现Runnable接口;
  2. 重写run()方法;
  3. 创建RunableTask对象;
  4. 创建Thread类的对象,将RunableTask对象作为构造方法的参数;
  5. 启动线程。

代码示例:

public class RunableTask implements Runnable {
    @Override
    public void run() {
        System.out.println(" 实现 Runnable接口 创建的线程,ThreadName:" + Thread.currentThread().getName()
                + "线程状态 state:" + Thread.currentThread().getState() +
                " 线程的上下文类加载器: "+Thread.currentThread().getContextClassLoader()
                +" 线程 Id :" + Thread.currentThread().getId() +
                "线程所属分组:" + Thread.currentThread().getThreadGroup() +
                "线程的优先级:" +Thread.currentThread().getPriority()
        );
    }

    public static void main(String[] args) {
        RunableTask runableTask = new RunableTask();
        Thread thread1 = new Thread(runableTask);
        Thread thread2 = new Thread(runableTask);
        Thread thread3 = new Thread(runableTask);
        thread1.start();
        thread2.start();
        thread3.start();


        System.out.println(" state :"+thread1.getState());
        System.out.println(" state :"+thread2.getState());
        System.out.println(" state :"+thread3.getState());
    }
}

在通过调用Thread.currentThread()方法,打印出线程相关的信息

 state :RUNNABLE
 实现 Runnable接口 创建的线程,ThreadName:Thread-2线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :11线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
 实现 Runnable接口 创建的线程,ThreadName:Thread-1线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :10线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
 实现 Runnable接口 创建的线程,ThreadName:Thread-0线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :9线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
 state :BLOCKED
 state :TERMINATED

在不指定线程名字和优先级的情况下,默认的线程名字为Thread- + 线程id,id从1开始自增。同时分配给线程默认分组 为main已经分组的优先级为10,线程的优先级统一为 5

实现Runnable接口这种方式出现的目的,是因为继承Thread类的方式具有一定的局限性。

理由:

  •        单继承的局限性,继承了Thread类,就无法继承其他类了,限制了可扩展性。
  •        从代码架构上考虑,具体执行任务应该(run方法)和线程解耦的。
  •        从性能损耗上来讲,如果每次任务都新建一个独立的线程,这种方式对性能开销比较大。因为线程,创建,执行和销毁都需要占用资源。

3.4 通过实现Callable接口实现线程

Callable接口中只有一个 call() 方法,该方法是抛出异常的,源码如下:

package java.util.concurrent;

@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能返回任务线程执行结果,而Runable不能返回
  • Callable的call方法允许抛出异常,而Runable异常只能在run方法内部消化

Callable需要和FutureTask搭配使用。

通过实现 Callable 接口实现多线程的步骤如下:

  1.     创建 CallableTask 类实现 Callable 接口。
  2.     创建 CallableTask 类的实例对象 callableTask 。
  3.     把实例对象 callableTask 作为参数来创建 FutureTask 类的实例对象 futureTask。
  4.     把实例对象 futureTask 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
  5.     调用 start() 方法,启动线程。
  6.     调用FutureTask类对象的get()方法来获取线程执行的返回值,即任务类对象中call()方法的返回值。

代码示例如下:

package com.copy.thread;

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

public class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(" 实现 Runnable接口 创建的线程,ThreadName:" + Thread.currentThread().getName()
                + "线程状态 state:" + Thread.currentThread().getState() +
                " 线程的上下文类加载器: "+Thread.currentThread().getContextClassLoader()
                +" 线程 Id :" + Thread.currentThread().getId() +
                "线程所属分组:" + Thread.currentThread().getThreadGroup() +
                "线程的优先级:" +Thread.currentThread().getPriority()
        );
        int a = 4;
        int b = 3;
        return a+b;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTask callableTask = new CallableTask();
        FutureTask<Integer> futureTask = new FutureTask<>(callableTask);
        //创建线程
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(" 返回值 :" + futureTask.get());
    }
}

结果:

实现 Callable接口 创建的线程,

线程名字 Thread Name:Thread-0

线程状态 Thread State:RUNNABLE

线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2

线程 Id :9

线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]

线程的优先级:5

返回值 :7

问题:

1、通过代码的流程,可以看出,最终任务的执行还是调用的Thread的start方法进行执行,那么FutureTask 和 Callable是如何实现多线程的?

  • 因为FutureTask实现了RunnableFuture , RunnableFuture 又继承了Runnable接口,所以,Thread构造方法中我们就可以传递Runnable接口的子类FutureTask,最终调FutureTask用子类重写RunnableFuture 的run方法,而在FutureTask的run方法中,我们最终又调用了属性Callable的call方法,最终实现多线程
  • 在FutureTask的run方法中,除了执行属性Callable的run方法,我们还做了进一步扩展,比如当call方法执行完毕后初始化result返回值属性,从而得到返回值
  • 设计模式——>适配器模式,Callable接口和Runnable接口,两者本身无关联,通过Runnable接口的实现类的run方法中调用call方法,最终实现关联,这是典型的适配器模式,把Callable call接口和Runnable run接口进行适配并做扩展,Future就相当于一个适配器.

FutureTask 的UML结构如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值