© 2020 GitHub, Inc.

Java多线程

in Read with 0 comments

Java多线程

进程与线程

多线程的好处

进程与线程的区别

进程是一个独立的运行环境,而线程是在进程中执行的一个任务。本质区别是是否单独占用内存地址空间及其它系统资源(比如I/O):

上下文切换

​ 有时也称作进程切换或任务切换,是指CPU从一个进程(或线程)切换到另一个进程(或线程)。上下文是指某一时间点CPU寄存器和程序计数器的内容

​ CPU为每个线程分配CPU时间片来实现多线程机制。CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。在切换下一个任务前,会保存上一个任务的状态。,在下次切换到该任务时,在加载该任务的状态。任务从保存再到加载的过程就是一次上下文切换

​ 上下文切换通常是计算密集型的,意味着此操作会消耗大量的CPU时间,故线程也不是越多越好

纤程

    // todo

Java多线程实现方法

Thread和Runnable

实现线程类

  1. 继承Thread,重写run()方法;
  2. 实现Runnable接口的run()方法。
public class Demo {
  public static class ThreadDemo extends Thread {
    @Override
    public void run() {
      System.out.println("我是一个线程类");
    }
  }
  public static void main (String[] args) {
    // 只有在调用了strat方法才算启用了该线程(虚拟机会先创建一个线程,然后等到该线程得到时间片再调用run()方法),多次调用start()方法会抛出IllegalThreadStateException
        new ThreadDemo().start();
  } 
}
public class Demo {
  public static class ThreadDemo implements Runnable {
    @Override
    public void run() {
      System.out.println("我是一个线程类");
    }
  }
  public static void main (String[] args) {
        new ThreadDemo().start();
    new Thread(() -> {
      System.out.println("Java 8 lambda");
    })
  } 
}

构造方法

    // 支持ThreadLocal的属性
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    // 常用构造方法,多个重载,详见源码
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 初始化线程
     * @param g         线程组--指定该线程再哪个线程组下
     * @param target    指定要执行的任务
     * @param name      指定线程名字
     * @param stackSize 新线程所需的堆栈大小,或者为零以指示要忽略此参数 。
     *                  默认为0,由native方法自动决定堆栈大小.
     * @param acc       用于初始化私有变量inheritedAccessControlContext(在init方法初始化,在exit            
     *                  方法设置成null) 
     * @param inheritThreadLocals 可继承的TreadLocal
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        // 实现详见源码
    }
    // 根据acc初始化用于初始化私有变量inheritedAccessControlContext
    this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();

start()方法

    private volatile int threadStatus = 0; // 这个是用于记录线程状态的
        /**
            * 线程的启动方法
            */
         public synchronized void start() {
        // 0代表状态"NEW",当第二次调用start()时,threadStatus就不是0了是5(RUNNABLE),2是(TERMINATED)
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
              // 是一个native方法,会更改threadStatus的值
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {     
            }
        }
    }

常用方法

Thread和Runnable比较

Callable、Future和FutureTask

Callable和Future会在线程执行完成后,有一个返回值。

/**
    * Callable接口定义
    */
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Callable例子

// 自定义Callable
class CallableDemo implements Callable<String>{
    @Override
    public String call() throws Exception {
        TimeUnit.SECONDS.sleep(1000);
        return "我是返回值";
    }
    public static void main(String args[]) {
        // 使用线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        CallableDemo demo = new CallableDemo();
        Future<String> result = executor.submit(demo);
        try {
            // get方法会阻塞当前线程,直到得到结果。
            System.out.println(result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        try {
            // 所以实际编码中建议使用可以设置超时时间的重载get方法。
            System.out.println(result.get(100, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

Future接口定义

public interface Future<V> {
      // 尝试取消这个任务的执行,参数是是否采用中断方式取消线程执行
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

由于实现Future接口需要实现很多方法比较麻烦,一般使用它的实现类FutureTask(该类实现了RunnableFuture接口)

public class Demo implements Callable<String> {
    public static void main(String[] args){
        ExecutorService executor = Executors.newCachedThreadPool();
        FutureTask<String> task = new FutureTask<String>(new Demo());
        executor.submit(task);
        try {
            // get方法会阻塞当前线程,直到得到结果。
            System.out.println(task.get());
            System.out.println(task.get(1, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    @Override
    public String call() throws Exception {
        return "返回值";
    }
}

回复