Java 并发编程笔记 - 创建线程的 ?种方法

Posted 笑虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 并发编程笔记 - 创建线程的 ?种方法相关的知识,希望对你有一定的参考价值。

Java 并发编程笔记 - 创建线程的 ?种方法

严格的说创建线程只有实现 Runnable 接口一种方法,其他都是间接实现 Runnable

  1. Thread 实现 Runnable
  2. Callable 是无法直接起的,要通过 RunnableFuture 联姻。(FutureTask 实现了 RunnableFuture )
  3. 线程池创建直接返回的都是 Future 可想而知。

Thread 类

Java 只能单继承,所以一般不会这么用。

  1. 直接继承 Thread 重写 run
  2. 再调 start() 启动
public class ThreadDemo extends Thread 
    private int i = 10;

    @Override
    public void run() 
        while (i > 0) 
            System.out.println("线程 " + Thread.currentThread().getName() + " 输出:"  + i);
            i--;
        
    

    public static void main(String[] args) throws InterruptedException 
        System.out.println("线程 " + Thread.currentThread().getName() + "开始。");
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
    

Runnable 接口

  1. 实现 Runnable 重写 run
  2. 没有返回值
  3. 启动:两种
    3.1. 丢进 Thread.start()
    3.2. 用 ExecutorService 启动线程

public class RunnableDemo implements Runnable
    private int i = 10;

    @Override
    public void run() 
        while (i > 0) 
            System.out.println("线程 " + Thread.currentThread().getName() + " 输出:"  + i);
            i--;
        
    

    public static void main(String[] args) throws InterruptedException 
        // 用 Thread.start() 启动
        System.out.println("线程 " + Thread.currentThread().getName() + "开始。");
        new Thread(new RunnableDemo()).start();
        // Runnable 也可以用 ExecutorService 启动线程,比 Thread 的 start() 更好
        ExecutorService executor = Executors.newCachedThreadPool();
        RunnableDemo runnableDemo = new RunnableDemo();
        executor.execute(runnableDemo);
        executor.shutdown();
    

Callable 接口

  1. 实现 Callable<T> 重写 call
  2. 有返回值。返回值类型 T。大概套路:
    2.1. 提前说明一下关系:FutureTask implements RunnableFuture extends Runnable, Future。(血脉凑齐)
    2.2. 先用Callable对象作为参数实例化一个FutureTask。拿到一个Future对象,它有两个用途:
    2.2.1. 首先Future对象是对将来的一个抽象,所以将来可以通过它获得最终结果。(是不是有Promise的感觉)
    2.2.2. 其次FutureTaskRunnable的实现,所以可以丢进 Thread
  3. 启动三种:
    3.1. 用FutureTask包装一下丢进 Thread.start()
    3.2. 用 ExecutorService 启动线程
    3.3. new Callable() 先丢进 FutureTask 再交给 ExecutorService 提交
  4. Future<T>.get() 获取线程返回值
public class CallableDemo implements Callable<Integer> 
    private int i = 10;

    @Override
    public Integer call() throws Exception 
        while (i > 0) 
            System.out.println("线程 " + Thread.currentThread().getName() + " 输出:"  + i);
            i--;
        
        return 999;
    

    public static void main(String[] args) throws InterruptedException, ExecutionException 
        System.out.println("线程 " + Thread.currentThread().getName() + "开始。");
        
        // FutureTask 是 Runnable 的实现,所以也可以丢给 Thread().start();
        new Thread(new FutureTask<Integer>(new CallableDemo()), "AAA").start();
        
        // Callable 只能用 ExecutorService 启动线程
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<Integer> submit = executor.submit(new CallableDemo());
        executor.shutdown();
        System.out.println("线程结束,返回:" + submit.get());

        // 先丢进 FutureTask 再提交
        ExecutorService executor2 = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new CallableDemo());
        executor2.submit(futureTask);
        executor2.shutdown();
        System.out.println("线程结束,返回:" + futureTask.get());
    

多线程虚假唤醒

对比两个方法对wait();的判断处理。
吃粉()的判断在if 代码块中,下次醒来时,会直接往下走。但我们想要的是被唤醒后,首先判断是否满足条件,满足的情况下才处理业务。所以这里会有漏网之鱼。
下粉()因为用的while判断,下次原地醒来,继续走完代码块,又回头判断一次。必须完足条件才会继续,不存在漏网之鱼。

class 牛肉粉 
    private int i = 0;

    public synchronized void 吃粉() throws InterruptedException 
        if (i == 0) 
            // 在 wait() 调用时,释放锁,就地睡觉。
            this.wait();
            // 被唤醒时,原地站起来紧跟在 wait()后继续执行。
        
        System.out.println(i + " 碗库存!" + Thread.currentThread().getName() + " 吃了一碗粉。");
        i--;
        this.notifyAll();
    

    public synchronized void 下粉() throws InterruptedException 
        while (i != 0) 
            this.wait();
        
        System.out.println(i + " 碗库存!" + Thread.currentThread().getName() + " 下了一碗粉。");
        i++;
        this.notifyAll();
    

参考资料

B站:【狂神说Java】JUC并发编程最新版通俗易懂

以上是关于Java 并发编程笔记 - 创建线程的 ?种方法的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程笔记4-线程池

Java并发编程笔记 阻塞和中断

并发编程(学习笔记-Java线程)-part2

Java并发编程的艺术读书笔记——Java并发编程基础

Java并发编程学习笔记

Java 并发编程一文详解 Java 中有几种创建线程的方式