并发、线程、线程池、锁相关

一。并发、并行、串行

并发:宏观上并行,微观上串行

并行:一起执行

串行:交替执行

二。创建线程的几种方式

1. 继承Thread类(最基础)

// 1. 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行: " + Thread.currentThread().getName());
    }
}

// 使用
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();  // 启动线程,自动调用run()方法
        
        // 输出: 线程执行: Thread-0
    }
}

2. 实现Runnable接口(推荐)

// 2. 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable线程: " + Thread.currentThread().getName());
    }
}

// 使用
public class RunnableDemo {
    public static void main(String[] args) {
        // 方式1: 单独类
        Thread thread1 = new Thread(new MyRunnable());
        thread1.start();
        
        // 方式2: 匿名内部类
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名Runnable线程");
            }
        });
        thread2.start();
        
        // 方式3: Lambda表达式(Java 8+)
        Thread thread3 = new Thread(() -> {
            System.out.println("Lambda线程: " + Thread.currentThread().getName());
        });
        thread3.start();
    }
}

3. 实现Callable接口 + FutureTask(有返回值)



// 3. 实现Callable接口(带返回值)
class MyCallable implements Callable<String> {
    private final String taskName;
    
    public MyCallable(String taskName) {
        this.taskName = taskName;
    }
    
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);  // 模拟耗时操作
        return taskName + " 完成,线程: " + Thread.currentThread().getName();
    }
}

// 使用
public class CallableDemo {
    public static void main(String[] args) throws Exception {
        Callable<String> callable = new MyCallable("任务1");
        FutureTask<String> futureTask = new FutureTask<>(callable);
        
        Thread thread = new Thread(futureTask);
        thread.start();
        
        // 获取返回值(会阻塞直到任务完成)
        String result = futureTask.get();
        System.out.println("结果: " + result);
        
        // 输出: 结果: 任务1 完成,线程: Thread-0
    }
}

三。线程池

1.几种线程池类型

public class ThreadPoolTypes {
    public static void main(String[] args) {
        // 5.1 固定大小线程池
        ExecutorService fixedPool = Executors.newFixedThreadPool(5);
        
        // 5.2 缓存线程池(自动扩容收缩)
        //来任务自动创建线程,没任务线程存活时间到了就自己销毁
        ExecutorService cachedPool = Executors.newCachedThreadPool();
        
        // 5.3 单线程池(保证顺序执行)
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        
        // 5.4 定时任务线程池
        ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
        
        // 定时任务示例
        scheduledPool.schedule(() -> {
            System.out.println("延迟3秒执行");
        }, 3, TimeUnit.SECONDS);
        
        // 周期性任务
        scheduledPool.scheduleAtFixedRate(() -> {
            System.out.println("每2秒执行一次");
        }, 1, 2, TimeUnit.SECONDS);
    }
}

2.线程池提交任务

a.无返回值execute提交

ExecutorService executor = Executors.newFixedThreadPool(3);

// 提交Runnable任务(无返回值)
executor.execute(() -> {
    System.out.println("执行任务: " + Thread.currentThread().getName());
    // 模拟业务逻辑
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

// 可以连续提交多个任务
for (int i = 0; i < 5; i++) {
    final int taskId = i;
    executor.execute(() -> {
        System.out.println("任务" + taskId + " 在 " + 
                          Thread.currentThread().getName() + " 执行");
    });
}

b.有返回值submit提交

ExecutorService executor = Executors.newFixedThreadPool(3);

// 提交Callable任务(有返回值)
Future<String> future = executor.submit(() -> {
    System.out.println("Callable任务开始执行");
    Thread.sleep(2000); // 模拟耗时操作
    return "任务完成结果: " + Thread.currentThread().getName();
});

// 继续执行其他逻辑(不阻塞)
System.out.println("主线程继续执行...");

try {
    // 需要结果时获取(会阻塞)
    String result = future.get(); // 阻塞直到任务完成
    System.out.println("获取结果: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

3.线程池7个参数

好的,这是 Java 线程池七大参数的总结表格,包含参数名、含义和一个形象的比喻。

Java 线程池七大参数一览表

参数名

数据类型

含义

通俗比喻

​核心线程数

int

线程池中保持常驻的核心线程数量,即使它们处于空闲状态也不会被回收(除非设置allowCoreThreadTimeOuttrue)。

公司的正式员工

最大线程数

int

线程池允许创建的最大线程数量。核心线程用完后,任务队列也满了,会创建新线程直到达到此上限。

公司最大员工数(正式+临时)​

​线程存活时间

long

当线程数超过 corePoolSize时,多余的空闲线程等待新任务的最长时间。超时后,这些线程将被终止。

临时工的空闲等待时间

​时间单位

TimeUnit

keepAliveTime参数的时间单位(如秒、毫秒)。

存活时间的单位(如:小时)​

​工作队列

BlockingQueue<Runnable>

用于保存等待执行的任务的队列。这是一个关键的缓冲区。

产品待办需求列表

线程工厂

ThreadFactory

用于创建新线程的工厂。不同类型的工厂可以用于设置线程名、优先级、守护线程等,便于监控和调试。

HR / 招聘部门

​拒绝策略

RejectedExecutionHandler

当线程池和队列都已饱和,无法处理新任务时,执行的拒绝策略。(直接抛出异常由提交任务的线程完成静默丢弃丢弃最旧的任务添加新任务

公司满负荷时的接单策略

四。sleep、wait 、notify、notifyall、join、yield

调用wait会进入等待池,需要notify()或者notifyall唤醒。

方法

所属类

作用

释放cpu

释放锁

唤醒条件

使用场景

sleep()

Thread

线程休眠

释放,时间到结束

❌ 不释放

时间到期

定时任务

wait()

Object

线程等待

释放,被唤醒结束

✅ 释放

notify/超时

线程间协作

notify()

Object

唤醒单个线程

-

-

线程间通信

notifyAll()

Object

唤醒所有线程

-

-

多线程协作

join()

Thread

等待线程结束

-

线程终止

线程顺序执行

yield()

Thread

让出CPU,不一定能让出去

❌ 不释放

立即让出

线程优先级调整

五。常见代码

1.多个线程交替打印0-100

public class AlternatePrint {
    private static final Object lock = new Object();
    private static int count = 0;
    private static final int MAX = 100;

    public static void main(String[] args) {
        new Thread(() -> {
            while (count <= MAX) {
                synchronized (lock) {
                    if (count % 2 == 0) {
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                        lock.notify();
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        }, "线程A").start();

        new Thread(() -> {
            while (count <= MAX) {
                synchronized (lock) {
                    if (count % 2 == 1) {
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                        lock.notify();
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        }, "线程B").start();
    }
}

 五.锁、同步相关

1.相关概念:

可重入:同一个线程可以多次获取同一把锁。

public class ReentrantExample {
    public synchronized void methodA() {
        System.out.println("进入methodA");
        methodB(); // 同一个线程再次获取锁
        System.out.println("离开methodA");
    }
    
    public synchronized void methodB() {
        System.out.println("进入methodB");
        // 可以正常执行,不会死锁
        System.out.println("离开methodB");
    }
}

公平锁:维护一个线程等待队列来实现。按照线程请求锁的顺序分配(先到先得)

非公平锁:不保证顺序,可能允许新请求的线程插队,公平和非公平只体现在新线程是否能竞争锁,但是如果有线程让出来cpu资源,队列线程唤醒了还是第一个

偏向锁:JVM 对 synchronized 的一种优化机制,当一个线程第一次获取偏向锁时,JVM 会记录该线程的 ID。如果该线程再次请求锁,JVM 会直接允许它获取锁,而无需进行任何同步操作.,就是减少了不断的CAS自旋开销,只在第一个获取锁的过程中判断。如果有其他线程尝试获取锁,偏向锁会升级为轻量级锁,用CAS来实现,避免线程阻塞。当更多线程竞争锁资源,轻量级锁会转向重量级锁,用互斥来实现。会导致线程阻塞。

可中断锁:lock.lockInterruptibly()。可中断锁允许线程在等待锁的过程中被中断。

方法

类型

作用对象

返回值

是否清除中断状态

典型使用场景

thread.interrupt()

实例方法

目标线程(thread)

void

不涉及

主动请求中断另一个线程

Thread.interrupted()

静态方法

当前线程

boolean

会清除

检查并清除当前线程的中断状态(一次性检查)

thread.isInterrupted()

实例方法

目标线程(thread)

boolean

不会清除

持续监控某线程的中断状态

Thread thread = new Thread(() -> {
    while (true) {
        System.out.println("Running..."); // 不检查中断标志
    }
});
thread.start();
thread.interrupt(); // 线程不会停止!
=======================================================================================
Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) { // 检查中断标志
        System.out.println("Working...");
    }
    System.out.println("线程收到中断请求,优雅退出");
});
thread.start();//主线程调用子线程的执行,二者会同时执行
thread.interrupt(); // 线程会退出,此时也是主线程中断子线程

超时锁:lock.tryLock(long, TimeUnit),超时锁允许线程在尝试获取锁时设置一个超时时间。如果在该时间内未能获取锁,线程会放弃等待并继续执行其他操作。

自旋锁线程获取锁的过程中,当没获取到,就循环获取。通过CAS进行完成的。不会阻塞线程。

注意:CAS跟自旋锁是两个东西,自旋锁可以说是使用了CAS,没改变就循环获取继续用CAS。

AQS:用于构建锁和其他同步器(如 ReentrantLockSemaphoreCountDownLatch 等), 提供了一个框架,开发者可以基于它实现自定义的同步器。

  • 使用一个 ​共享的同步状态(state)​ 来表示锁的状态。
  • 通过一个双向链表组成的 ​FIFO(先进先出的) 队列 管理等待获取锁的线程。
  • 使用CAS操作来获取锁(改变锁的状态)。
  • 共享模式:允许多个线程同时获取锁。
  • 独占模式:只允许一个线程获取锁。

volatile Java 中的一个关键字,主要用于解决 ​可见性 和 ​指令重排序 问题:

  1. 保证可见性:当一个线程修改了 volatile 变量的值,其他线程可以立即看到修改后的值。

  2. 禁止指令重排序volatile 变量的读写操作不会被 JVM 重排序,从而保证程序的执行顺序符合预期。

  3. 并不能替代锁,它只能保证可见性和禁止指令重排序,但无法保证原子性

  4. volatile 变量的读写操作直接作用于主内存,而不是线程的本地内存。
  5. 每次读取 volatile 变量时,线程都会从主内存中获取最新值。
  6. 每次写入 volatile 变量时,线程会立即将新值刷新到主内存中。

2.CAS

是一种原子操作,用于在多线程环境下实现无锁的线程安全操作。CAS操作通常用于实现非阻塞算法。

CAS操作包含三个操作数:

  1. 内存位置(V)​:需要更新的变量。
  2. 预期值(A)​:操作之前读取的变量值。
  3. 新值(B)​:如果内存位置的值等于预期值,则将其更新为新值。

CAS的操作逻辑如下:

  • 比较内存位置的值与预期值,如果相等,则将内存位置的值更新为新值。
  • 如果不相等,则不做任何操作。
  • 无论是否更新成功,CAS操作都会返回内存位置的当前值。
     AtomicInteger atomicInt = new AtomicInteger(0);
    // 预期值为0,新值为1
    boolean success = atomicInt.compareAndSet(0, 1);
    System.out.println("CAS操作是否成功: " + success); // 输出 true
    
      // 预期值为0,新值为2(此时实际值为1,所以操作失败)
       success = atomicInt.compareAndSet(0, 2);
      System.out.println("CAS操作是否成功: " + success); // 输出 false

AtomicInteger:原子整型类,它能够在多线程环境下保证对整型变量的操作具有原子性。 

3.Synchronized与ReentrantLock

    Synchronized是一个关键字,修饰代码块(对象)、静态方法(类)、成员方法(对象)。

    • 无需手动管理锁。
    • ​自动释放锁。
    • 非公平锁
    • ​可重入:同一个线程可以多次获取同一把锁。
    • ​不支持高级功能:可中断锁、超时锁等。
    • 原理:

    ReentrantLock

    • ReentrantLock 是一个类,实现了 Lock 接口。修饰代码块的。
    • 它是显式锁,需要手动获取和释放锁。
    • 可设置公平锁或者非公平锁
    • 提供了比 synchronized 更高级的功能,如可中断锁、超时锁等。
    • 可重入:同一个线程可以多次获取同一把锁。
    • 原理:

    synchronized和ReentrantLock都能锁什么?

    synchronized:锁类方法、实例方法、object对象

    lock:锁一段代码块

    4.CountDownLatch、CyclicBarrier、Semaphore的区别和底层原理

    4.1、CountDownLatch

    • CountDownLatch 的底层依赖于 ​AQS(AbstractQueuedSynchronizer)​,这是一个用于构建锁和同步器的框架。
    • CountDownLatch 通过 AQS 的共享模式来实现线程的等待和唤醒。
    • 常用于计数器来控制所有线程执行完毕CountDownLatch 维护一个计数器,设置初始值,进行减法操作。
    public class CountDownLatchDemo {
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(3); // 初始计数3
            
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + " 完成任务");
                    latch.countDown(); // 计数减1
                }).start();
            }
            
            latch.await(); // 主线程等待计数归零
            System.out.println("所有任务完成,主线程继续");
        }
    }
    
    Thread-0 完成任务
    Thread-1 完成任务  
    Thread-2 完成任务
    所有子线程完成任务,主线程继续执行

    4.2、CyclicBarrier

    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(3, () -> {
                System.out.println("所有线程到达屏障,执行屏障动作");
            });
            
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 到达屏障");
                        barrier.await(); // 等待其他线程
                        System.out.println(Thread.currentThread().getName() + " 继续执行");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    Thread-0 到达屏障
    Thread-1 到达屏障
    Thread-2 到达屏障
    所有线程到达屏障,执行屏障动作
    Thread-2 继续执行
    Thread-0 继续执行  
    Thread-1 继续执行
    a、被动重置计数器
    public class CyclicBarrierReuseDemo {
        public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(2, () -> {
                System.out.println("屏障已触发,准备下一轮");
            });
    
            // 第一轮
            new Thread(() -> awaitAtBarrier(barrier, "线程A-第一轮")).start();
            new Thread(() -> awaitAtBarrier(barrier, "线程B-第一轮")).start();
    
            // 第二轮(自动重用)
            new Thread(() -> awaitAtBarrier(barrier, "线程A-第二轮")).start();
            new Thread(() -> awaitAtBarrier(barrier, "线程B-第二轮")).start();
        }
    
        private static void awaitAtBarrier(CyclicBarrier barrier, String threadName) {
            try {
                System.out.println(threadName + " 到达屏障");
                barrier.await();
                System.out.println(threadName + " 冲破屏障");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    线程A-第一轮 到达屏障
    线程B-第一轮 到达屏障
    屏障已触发,准备下一轮
    线程B-第一轮 冲破屏障
    线程A-第一轮 冲破屏障
    线程A-第二轮 到达屏障
    线程B-第二轮 到达屏障
    屏障已触发,准备下一轮
    线程B-第二轮 冲破屏障
    线程A-第二轮 冲破屏障
    b、主动重置计数器

    通过 reset()方法可强制重置屏障(中断当前轮次,所有等待线程收BrokenBarrierException)。

    CyclicBarrier barrier = new CyclicBarrier(3);
    
    new Thread(() -> {
        try {
            barrier.await();
        } catch (BrokenBarrierException e) {
            System.out.println("线程被中断:屏障已重置");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
    
    barrier.reset(); // 强制重置
    c、二者区别:

    对比点

    CountDownLatch

    CyclicBarrier

    核心目的

    主等子

    子等子

    重用性

    一次性

    可循环

    计数方向

    递减(countDown()

    递增(await()调用次数)

    异常影响

    局部

    全局(需重置)

    典型场景

    启动检查、任务汇总

    并行计算、回合制同步

    4.3、Semaphore

    用于 ​控制并发访问资源 的工具类。它通过维护一组 ​许可证(permits)​ 来限制同时访问资源的线程数量

    • Semaphore 的底层依赖于 ​AQS(AbstractQueuedSynchronizer)​
    • Semaphore 通过 AQS 的共享模式来实现许可证的获取和释放。
    • 通过控制许可证的获取、释放来控制并发线程的数量。

    六、死锁产生的四个必要条件(缺一不可)

    1. 互斥条件​:一个资源每次只能被一个线程使用。

    2. 请求与保持条件​:一个线程因请求资源而阻塞时,对已获得的资源保持不放

    3. 不剥夺条件​:线程已获得的资源,在未使用完之前,不能被其他线程强行剥夺

    4. 循环等待条件​:若干线程之间形成一种头尾相接的循环等待资源关系

    只要打破以上四个条件中的任意一个,死锁就能被预防。​

    解决思路:

    解决请求与保持、不可剥夺:使用可超时的锁(ReentrantLock.tryLock

    解决循环等待:避免嵌套加锁,设计时保证锁顺序一致(最有效、最常用),强制所有线程以全局统一的顺序获取锁。

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值