Java 多线程 - 学习笔记

Posted 笑虾

tags:

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

Java 多线程 - 学习笔记

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
  3. 启动三种:
    3.1. 丢进 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多线程/并发学习笔记

java学习笔记 --- 多线程(多线程的控制)

Java多线程编程(学习笔记)

Java学习笔记—多线程

Java多线程学习笔记

Java学习笔记44(多线程一)