首页 > 编程语言 >Java多线程

Java多线程

时间:2024-09-05 20:21:51浏览次数:6  
标签:Java 并发 任务 线程 new 多线程

Java多线程

什么是多线程和线程安全?

多线程

多线程(Multithreading)是指在一个程序中同时运行多个线程的技术。线程是操作系统能够独立管理的最小执行单位,一个程序可以包含一个或多个线程。多线程的好处是可以充分利用多核处理器的性能,提高程序的执行效率,尤其是在处理 I/O 密集型任务时,多线程可以减少等待时间。

多线程的特点:
  1. 并发执行:多个线程可以同时执行,提升性能。
  2. 共享内存:多个线程可以共享同一进程的内存空间,包括全局变量、静态变量等。
  3. 上下文切换:线程之间会进行上下文切换,操作系统负责分配 CPU 资源。

线程安全

线程安全(Thread Safety)是指多个线程同时访问共享资源时,不会因为竞态条件(Race Condition)导致程序出现不正确的行为。在多线程环境中,线程之间可能会同时访问或修改共享数据,如果不进行适当的同步控制,可能导致数据不一致或程序异常。

线程安全的实现方式:
  1. 同步机制:通过同步关键字(如 synchronized)来保证同一时间只能有一个线程访问共享资源。例如:

    synchronized (this) {
        // 临界区代码,只有一个线程可以执行
    }
    
  2. 锁(Lock)机制:使用 java.util.concurrent 包中的 ReentrantLock 等类,提供更灵活的锁定机制。

    Lock lock = new ReentrantLock();
    lock.lock();
    try {
        // 临界区代码
    } finally {
        lock.unlock();
    }
    
  3. 原子操作:使用 AtomicIntegerAtomicReference 等类来保证对变量的操作是原子的。

  4. 并发容器:使用线程安全的容器,如 ConcurrentHashMapCopyOnWriteArrayList 等,这些类已经为并发访问做了适当的控制。

举例说明:

如果多个线程同时对一个共享变量进行写操作,而没有采取同步机制,可能会出现数据覆盖或读取错误。例如:

public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

在多线程环境下,如果没有 synchronized 来保护 increment() 方法,多个线程可能同时读取和修改 count,导致最终结果不准确。

Java实现线程安全的几种方式

在Java中,实现线程安全的关键是确保多个线程在并发访问共享资源时不会引发数据不一致或冲突问题。实现线程安全的方式主要有以下几种:

1. 使用同步块(synchronized

  • 方法同步: 可以在方法声明上添加 synchronized关键字,表示该方法只能由一个线程访问。

    public synchronized void method() {
        // 同步代码
    }
    
  • 同步块: 可以在方法内部对某个共享资源加锁,使用 synchronized块来限定同步范围,避免整个方法都被锁住,提升效率。

    public void method() {
        synchronized (this) {
            // 同步代码
        }
    }
    

2. 使用显式锁(Lock接口)

  • java.util.concurrent.locks.Lock接口提供了更灵活的锁机制,相对于 synchronized块,Lock允许在不同位置加锁和解锁。

    Lock lock = new ReentrantLock();
    
    public void method() {
        lock.lock();
        try {
            // 同步代码
        } finally {
            lock.unlock();  // 确保锁一定会释放
        }
    }
    

3. 使用并发容器

Java的java.util.concurrent包提供了多个线程安全的容器类,内部使用了更高效的同步机制,如:

  • ConcurrentHashMap
  • CopyOnWriteArrayList
  • ConcurrentLinkedQueue

这些类已经是线程安全的,适用于高并发场景,避免自己手动加锁。

4. 原子类(Atomic包)

java.util.concurrent.atomic包提供了一系列原子操作类,如AtomicIntegerAtomicLongAtomicReference等,这些类通过CAS(Compare-And-Swap)操作实现了非阻塞的线程安全操作。

AtomicInteger count = new AtomicInteger(0);

public void increment() {
 count.incrementAndGet();
}

5. 线程本地变量(ThreadLocal

ThreadLocal为每个线程提供独立的变量副本,使得每个线程访问到的变量是各自独立的,不会相互干扰。

private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

public void method() {
 threadLocal.set(threadLocal.get() + 1);
}

6. 使用volatile关键字

volatile用于修饰共享变量,确保线程对该变量的修改会立刻被其他线程可见。但它仅能保证可见性,不能保证原子性,适合用于状态标志等简单场景。

private volatile boolean flag = true;

public void stop() {
 flag = false;
}

7. Thread.join()wait()/notify()

  • join():可以确保一个线程执行完毕后,其他线程再继续执行。
  • wait()notify():配合 synchronized使用,可以实现线程之间的协调通信。

综合以上方法,具体使用哪种方式取决于应用场景和对性能的要求。常用的方式包括 synchronizedLock接口和并发容器等。

Java常见的并发工具类

Java中提供了丰富的并发工具类,主要集中在java.util.concurrent包中,涵盖了线程池、并发数据结构、同步控制工具等,用来简化并发编程并提升性能。以下是常见的并发工具类:

1. 线程池工具类

  • ExecutorService: 一个用于管理线程的接口,可以提交任务并让线程池执行。

  • Executors: 提供创建常见线程池的方法,如newFixedThreadPool()newCachedThreadPool()newSingleThreadExecutor()等。

    ExecutorService executor = Executors.newFixedThreadPool(5);
    executor.submit(() -> {
        // 任务代码
    });
    

2. 并发集合类

  • ConcurrentHashMap: 线程安全的哈希表,支持并发读写操作,内部通过分段锁机制来提高并发性能。
  • CopyOnWriteArrayList: 适用于读操作远多于写操作的场景,写操作时会复制整个数组,读操作无锁。
  • ConcurrentLinkedQueue: 无界的、基于链接节点的线程安全队列,适合高并发场景。

3. 同步控制工具类

  • CountDownLatch: 一个同步辅助工具,允许一个或多个线程等待,直到其他线程执行完操作(通过倒计数为0)。

    CountDownLatch latch = new CountDownLatch(3);
    latch.await(); // 等待,直到计数器为0
    latch.countDown(); // 计数器减1
    
  • CyclicBarrier: 类似于CountDownLatch,但它可以重复使用。多个线程相互等待,直到都到达屏障点,才继续执行。

    CyclicBarrier barrier = new CyclicBarrier(5);
    barrier.await(); // 所有线程到达屏障时同时继续
    
  • Semaphore: 信号量,用于控制同时访问某一资源的线程数量。可用于实现限流等场景。

    Semaphore semaphore = new Semaphore(2); // 允许2个线程同时访问
    semaphore.acquire(); // 获取许可
    semaphore.release(); // 释放许可
    
  • Exchanger: 用于两个线程之间交换数据,两个线程必须都调用exchange()方法后才能完成交换。

    Exchanger<String> exchanger = new Exchanger<>();
    String data = exchanger.exchange("Thread1 Data");
    

4. 原子类

java.util.concurrent.atomic包提供了线程安全的原子操作类,适用于需要高效进行简单计数或更新的场景:

  • AtomicIntegerAtomicLong: 原子更新基本数据类型的值。
  • AtomicReference: 原子更新引用类型。
  • AtomicStampedReference: 可以解决ABA问题的原子引用类,通过版本号来确保操作的原子性。

5. 并发锁

  • ReentrantLock: 可重入锁,相对于synchronized提供了更灵活的锁机制,例如可以设置超时时间、获取锁状态等。

    ReentrantLock lock = new ReentrantLock();
    lock.lock();
    try {
        // 同步代码
    } finally {
        lock.unlock();
    }
    
  • ReadWriteLock: 读写锁,允许多个读线程并发访问,但写线程是独占的,适用于读操作远多于写操作的场景。

    ReadWriteLock rwLock = new ReentrantReadWriteLock();
    rwLock.readLock().lock(); // 获取读锁
    rwLock.writeLock().lock(); // 获取写锁
    

6. Fork/Join框架

  • ForkJoinPool: 支持分治(Divide and Conquer)任务模型的线程池,特别适用于处理递归任务。

  • ForkJoinTask: 包含两种类型的任务:RecursiveTask(有返回值)和RecursiveAction(无返回值),用于并行处理任务。

    ForkJoinPool pool = new ForkJoinPool();
    pool.invoke(new RecursiveTask<>() {
        @Override
        protected Integer compute() {
            // 递归任务
        }
    });
    

7. ScheduledExecutorService

  • ScheduledExecutorService: 线程池接口,可以调度任务在给定的延迟后执行,或定期执行。

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    scheduler.scheduleAtFixedRate(() -> {
        // 定期执行任务
    }, 1, 5, TimeUnit.SECONDS);
    

8. CompletionService

  • ExecutorCompletionService: 用于管理异步任务的执行和结果收集,它包装了线程池,允许提交任务并在完成时检索结果。

    ExecutorCompletionService<Integer> service = new ExecutorCompletionService<>(executor);
    Future<Integer> result = service.submit(() -> {
        // 任务
        return 1;
    });
    

9. BlockingQueue

  • ArrayBlockingQueueLinkedBlockingQueue: 阻塞队列,支持线程安全的生产者-消费者模型。
  • PriorityBlockingQueue: 基于优先级的阻塞队列,元素按优先级排序。

10. Phaser

  • Phaser: 类似于CyclicBarrier,但更加灵活,支持动态调整参与线程的数量,并且可以分阶段执行任务。

    Phaser phaser = new Phaser(1); // 注册一个线程
    phaser.arriveAndAwaitAdvance(); // 到达并等待其他线程
    

这些工具类为Java开发者提供了丰富的并发编程手段,能够简化复杂的同步控制,并提高多线程程序的性能和稳定性。

Java创建线程池的几种方式

在 Java 中,线程池(Thread Pool)是用来管理和复用线程的机制。通过线程池,程序可以避免频繁地创建和销毁线程,提升性能,并有效控制线程的数量。Java 提供了多种方式来创建线程池,主要通过 java.util.concurrent.Executors 类和 ThreadPoolExecutor 类。

1. 使用 Executors 类创建线程池

Executors 提供了多种工厂方法用于创建不同类型的线程池,常用的有以下几种:

1.1 newFixedThreadPool(int nThreads)

创建一个固定大小的线程池。线程池中的线程数量固定,如果所有线程都在执行任务,新的任务将被放入队列中等待执行。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
  • 特点:线程数量固定,不会动态增加或减少。
1.2 newCachedThreadPool()

创建一个可缓存的线程池。如果线程池中有空闲线程会被重用,如果没有空闲线程则创建新的线程。当线程长时间空闲时,会自动回收。

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  • 特点:适合执行很多短期的异步任务,线程数量可以动态调整。
1.3 newSingleThreadExecutor()

创建一个只有一个线程的线程池。所有任务会按照提交的顺序依次执行。

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  • 特点:保证任务按顺序执行,适合需要串行执行任务的场景。
1.4 newScheduledThreadPool(int corePoolSize)

创建一个支持定时任务和周期性任务的线程池。适合需要按时间计划执行任务的场景。

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
  • 特点:可以延迟执行或周期性执行任务。

2. 使用 ThreadPoolExecutor 类自定义线程池

相比 Executors 提供的简单工厂方法,ThreadPoolExecutor 类可以精细化控制线程池的行为,通常用于更复杂的线程池配置。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    corePoolSize,       // 核心线程数
    maximumPoolSize,    // 最大线程数
    keepAliveTime,      // 线程空闲时间
    TimeUnit.SECONDS,   // 空闲时间的时间单位
    new ArrayBlockingQueue<>(100),  // 任务队列
    new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略
);
ThreadPoolExecutor 构造参数:
  1. corePoolSize:核心线程数,线程池中保持的最小线程数,即使线程处于空闲状态也不会被回收。
  2. maximumPoolSize:最大线程数,线程池中允许的最大线程数。
  3. keepAliveTime:非核心线程空闲的最大时间,超过该时间线程将被回收。
  4. unitkeepAliveTime 的时间单位,如 TimeUnit.SECONDS
  5. workQueue:任务队列,用于存储等待执行的任务。
  6. handler:拒绝策略,当任务过多且线程池已经饱和时,如何处理新任务。常用的策略有:
    • AbortPolicy:抛出异常(默认)。
    • CallerRunsPolicy:由提交任务的线程来执行任务。
    • DiscardPolicy:直接丢弃任务。
    • DiscardOldestPolicy:丢弃最旧的任务,然后尝试执行新的任务。

3. ForkJoinPool (Java 7 引入)

ForkJoinPool 是一个特殊的线程池,主要用于执行分而治之的任务(Divide and Conquer)。ForkJoinPool 支持任务的并行拆分,适合处理递归任务。

ForkJoinPool forkJoinPool = new ForkJoinPool();
  • 特点:适合 CPU 密集型任务,将任务拆分为多个子任务并行执行。

4. 自定义线程工厂

有时需要对线程池中的线程进行自定义配置,可以使用 ThreadFactory 自定义线程的创建过程。

ThreadFactory threadFactory = new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setName("CustomThread-" + thread.getId());
        return thread;
    }
};

ExecutorService customThreadPool = new ThreadPoolExecutor(
    5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), threadFactory
);

5. 选择线程池类型的建议

  • 如果任务数量较少且需要按顺序执行,使用 newSingleThreadExecutor
  • 如果任务数量不确定且任务执行时间较短,使用 newCachedThreadPool
  • 如果需要定时或周期性任务,使用 newScheduledThreadPool
  • 如果任务量大且需要精细控制线程池行为,使用 ThreadPoolExecutor

标签:Java,并发,任务,线程,new,多线程
From: https://blog.csdn.net/golove666/article/details/141939936

相关文章

  • 1.Javase入门基础
    Javase入门基础1.会常用的dos命令2.会安装java所需要的环境(jdk)3.会配置java的环境变量4.知道java开发三步骤5.会入门程序6.会三种注释方式7.Java入门程序所需要注意的地方8.println和print区别一、算机编程核心语法(固定格式)数据类型、运算符、流程控制、数组、方法二、面向对象核......
  • 计算机毕业设计:Java网上书城图书购物商城系统开题报告+源代码效果图
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......
  • Python 和 Java 区别是什么?哪个运行效率高?为什么?
    一、Python和Java的区别1.编程风格•Python是一种解释型动态编程语言,语法简洁、灵活,代码较为简洁直观,注重代码的可读性和简洁性,使用缩进来表示代码块。•Java是一种编译型静态编程语言,语法相对严谨,代码风格较为规范,需要明确声明变量类型,使用大括号来表示代码块。2.......
  • 多线程
    多线程进程和线程区别并行和并发区别创建线程的方式(高频)线程包含哪些状态,状态如何的变化(高频)搜生命周期现成顺序执行java中的wait和sleep方法的不同wait必须要和syn..锁一块使用,不然报错如何停止一个正在运行的线程线程安全问题synchronized关键字底层......
  • 基于java的个性化图书推荐系统(11181)
     有需要的同学,源代码和配套文档领取,加文章最下方的名片哦一、项目演示项目演示视频二、资料介绍完整源代码(前后端源代码+SQL脚本)配套文档(LW+PPT+开题报告)远程调试控屏包运行三、技术介绍Java语言SSM框架SpringBoot框架Vue框架JSP页面Mysql数据库IDEA/Eclipse开发......
  • Java中多态的学习
    多态目录多态多态的概念为什么要使用多态多态存在的三个必要条件多态的实现方式多态的分类方式一方式二多态的机制原理多态的概念多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。为什么要使用多态消除类型之间的耦......
  • JAVA记录
    记录工作中用到的一些工具、方法、问题等。远程调试远程调试 启动端口8084,debug监听端口是8085java-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8085-jar/opt/yinsuankeji/digit.jar--server.port=8084&JDK17安装#java17安装sudoaptupd......
  • Java运算符(详解)
    前言:    Java中运算符有哪些?    大致分为:    算术运算符、关系运算符、逻辑运算符、位运算符、移位运算符、条件运算符接下来,一一分析。算术运算符: 基本运算符:    加减乘除,是最基本的运算符。例子:publicstaticvoidmain(Str......
  • (免费源码)计算机毕业设计必看必学 原创定制程序 java、PHP、python、小程序、文案全套
    摘 要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对高校知识共享系统等问题,对高校知识共享系统进行研究分析,然后开发设计出高校知识共享系统以......
  • Java多线程
    进程and线程    进程是程序的一次动态执行过程。经历了从代码加载、执行到执行完毕的一个完整过程;(由于CPU的执行速度很快,使得所有的进程像是“同时”执行一样)多线程是实现并发机制的一种有效手段。线程是比进程更小的执行单位,多线程是指一个进程在执行过程中可以产......