java多线程(二)

 (六)wait和notify方法

在 Java 多线程编程中,wait() 和 notify()/notifyAll() 是 Object 类的核心方法,用于实现线程间的协作与通信。它们允许线程在特定条件下暂停执行(wait()),并在条件满足时被唤醒(notify()/notifyAll())。以下是详细介绍:

基本概念

  • wait():让当前线程释放对象锁,并进入该对象的等待队列(wait set),直到其他线程调用相同对象的 notify() 或 notifyAll()
  • notify():随机唤醒该对象等待队列中的一个线程,使其重新竞争对象锁。
  • notifyAll():唤醒该对象等待队列中的所有线程,让它们重新竞争对象锁。

关键特性

  • 必须在 同步块(synchronized) 中调用,否则会抛出 IllegalMonitorStateException
  • wait() 会释放对象锁,而 sleep() 或 join() 不会释放锁。

中断处理

若线程在 wait() 时被中断,会抛出 InterruptedException,需妥善处理。

notify() 和notifyAll()

  • notify():仅唤醒一个线程,适用于:
    • 多个线程等待同一条件,且只需唤醒一个。
    • 唤醒所有线程可能导致性能问题(如大量线程竞争锁)。
  • notifyAll():唤醒所有线程,适用于:
    • 多个线程等待不同条件,需全部唤醒。
    • 避免某些线程永久等待(如生产者 - 消费者模型中,生产者唤醒消费者,消费者唤醒生产者)。

(七)ThreadLocal

ThreadLocal 是 Java 中用于实现线程局部变量的类,它为每个使用该变量的线程都创建一个独立的副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。以下是关于 ThreadLocal 的详细解析:

1. 核心概念

  • 线程隔离:每个线程都拥有自己的独立变量副本,线程间互不影响。
  • 存储作用域:变量的作用域限定于当前线程,生命周期与线程一致。
  • 典型场景
    • 数据库连接(每个线程独立维护自己的连接)。
    • 用户会话管理(每个线程保存独立的用户信息)。
    • 日志上下文传递(如链路追踪 ID)。
    public static void main(String[] args){
        ThreadLocal<String> local = new ThreadLocal<>();//注意这是一个泛型类,存储类型为我们要存放的变量类型
        Thread t1 = new Thread(() -> {
            local.set("aaa");
            System.out.println("变量值已设定");
            System.out.println(local.get());
        });
        Thread t2 = new Thread(() -> {
            System.out.println(local.get());
        });
        t1.start();
        t2.start();
    }
}

/*
输出
变量值已设定
aaa
null
*/

ThreadLocal<String> local = new InheritableThreadLocal<>();--->子线程可以继承主线程

(八)定时器

标准库中的定时器

标准库中提供了一个Timer类。Timer类的核心方法为schedule,schedule 包含两个参数:第⼀个参数指定即将要执行的任务代码,第⼆个参数指定多长时间之后执行(单位为毫秒)。

Timer timer = new Timer();
timer.schedule(new TimerTask() {
 @Override
 public void run() {
 System.out.println("hello");
 }
}, 3000);

(九)守护线程

不要把操作系统的守护进程和守护线程相提并论

守护线程(Daemon Thread)是一种特殊的线程,它的主要作用是为其他线程提供服务,而不是执行核心业务逻辑。当所有非守护线程(用户线程)结束时,JVM 会自动终止守护线程,即使它们还在执行中。

通过 Thread.setDaemon(true) 方法将线程设置为守护线程,必须在启动线程前设置

    public static void main(String[] args) throws InterruptedException {
        Thread main = Thread.currentThread();

        Thread t = new Thread(() -> {
            try {
                while (true){
                    System.out.println("我是守护线程");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.setDaemon(true);
        t.start();

        Thread.sleep(3000);
    }

特性:

特性描述
线程类型判断thread.isDaemon():返回 true 表示守护线程,false 表示用户线程。
设置时机必须在 thread.start() 前调用 setDaemon(true),否则会抛出 IllegalThreadStateException
继承性线程创建的子线程会继承父线程的守护状态。例如,守护线程创建的子线程默认是守护线程。
终止机制当所有用户线程结束时,JVM 会自动调用 System.exit(0) 终止守护线程,不会等待守护线程完成。

(十)线程的状态

线程的生命周期主要有以下六种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed Waiting(计时等待)
  • Terminated(被终止)

在程序编码中想要确定线程当前的状态,可以通过getState()方法来获取,同时我们需要注意任何线程在任何时刻只能处于一种状态。

(十一)volatile关键字

在 Java 中,volatile 是一个轻量级的同步机制,用于保证变量的可见性和禁止指令重排序。它主要用于解决多线程环境下变量的一致性问题,但不能替代 synchronized 或锁机制。

核心概念

  • 可见性保证:当一个变量被声明为 volatile 时,JVM 会确保该变量的修改对所有线程立即可见。
  • 禁止指令重排序volatile 会禁止编译器和处理器对指令进行重排序,保证代码的执行顺序与书写顺序一致。
  • 原子性限制volatile 不保证原子性(除了对单个变量的写操作),如 count++ 这类复合操作仍需同步。

(十二) 多线程实战:生产者与消费者

模拟一个餐厅的2个厨师和3个顾客,假设厨师炒出一个菜的时间为3秒,顾客吃掉菜品的时间为4秒。

package com.test;

import java.util.Date;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;

public class Main6 {
    private static Queue<Object> queue = new LinkedList<>();

    public static void main(String[] args){
        new Thread(Main6::add,"厨师1").start();
        new Thread(Main6::add,"厨师2").start();

        new Thread(Main6::take,"消费者1").start();
        new Thread(Main6::take,"消费者2").start();
        new Thread(Main6::take,"消费者3").start();
    }

    private static void add(){
        while (true){
            try {
                Thread.sleep(3000);
                synchronized (queue){
                    String name = Thread.currentThread().getName();
                    System.out.println(new Date()+" "+name+"出餐了");
                    queue.offer(new Object());
                    queue.notifyAll();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void take(){
        while (true){
            try {
                synchronized (queue) {
                    while (queue.isEmpty()) queue.wait();
                    queue.poll();
                    String name = Thread.currentThread().getName();
                    System.out.println(new Date() + " " + name + "拿到了餐品,正在享用");
                }

                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值