为了账号安全,请及时绑定邮箱和手机立即绑定

Java高并发入门详解

标签:
Java 架构
概述

Java高并发入门介绍了高并发的基础概念、应用场景以及Java中的并发编程工具,包括synchronized关键字和volatile关键字等。文章还深入讲解了Java线程管理、常用并发模式与设计模式,并通过实战演练构建简单的高并发系统。

Java高并发基础概念

什么是高并发

高并发是指系统需要同时处理大量请求的能力,这在现代应用中越来越常见。例如,一个电商平台在双11活动期间,可能会在同一时间接收到数百万用户的访问请求。为了保证这些请求能够被快速响应,系统需要具备高并发处理能力。

高并发的意义和应用场景

高并发的意义在于提高系统的响应速度和处理能力,确保用户在使用系统时不会遇到长时间的等待或超时。高并发的应用场景包括但不限于:

  • 网络购物平台:如淘宝、京东等电商平台,在促销活动期间需要支持大量用户的并发请求。
  • 社交媒体平台:如微博、抖音等,需要在短时间内处理大量用户的数据请求。
  • 在线支付系统:如支付宝、微信支付等,在交易高峰期需要快速处理大量并发交易请求。
  • 金融系统:银行、证券交易所等金融系统,需要在短时间内处理大量用户的交易请求。

Java中常见的并发问题

在Java中,常见的并发问题包括但不限于:

  • 线程安全问题:当多个线程同时访问共享资源时,可能会出现数据不一致的问题。例如,多个线程同时修改同一个变量会导致数据混乱。
  • 死锁问题:多个线程互相等待对方释放资源,导致所有线程都无法继续执行。
  • 活锁问题:一种极端的情况,线程不停地执行,但是从不实际完成任何有用的工作。
  • 饥饿问题:某个线程因为其他线程的优先级过高或者其他原因长时间无法获得执行的机会。

示例代码:

public class ConcurrentExample {

    private int sharedResource;

    public void increment() {
        sharedResource++;
    }

    public void decrement() {
        sharedResource--;
    }
}

Java并发编程工具介绍

synchronized关键字

synchronized关键字是Java中的一个关键字,用于确保线程安全。它可以用于方法或者代码块。

  • 方法级别的同步:通过将方法声明为synchronized,可以确保同一时间内只有一个线程可以访问该方法。
  • 代码块级别的同步:通过synchronized代码块,可以指定锁定的范围,确保在同一时间内只有一个线程可以访问指定的代码块。

示例代码:

public class SynchronizedExample {

    public synchronized void method1() {
        // 只有当前线程能执行此方法
    }

    public void method2() {
        synchronized (this) {
            // 只有当前线程能执行此代码块
        }
    }

    public static synchronized void staticMethod1() {
        // 只有当前线程能执行此静态方法
    }

    public void method3() {
        synchronized (SynchronizedExample.class) {
            // 只有当前线程能执行此代码块
        }
    }
}

volatile关键字

volatile关键字用于确保变量的可见性和有序性。当一个变量被声明为volatile时,Java内存模型会确保所有线程对该变量的读写操作都是可见的,且不会被重排序。

示例代码:

public class VolatileExample {

    volatile boolean flag = false;

    public void method1() {
        flag = true;
    }

    public void method2() {
        while (!flag) {
            Thread.yield();
        }
    }
}

Java并发包(java.util.concurrent)简介

Java并发包提供了许多并发工具和框架,如ExecutorServiceFutureCountDownLatchSemaphore等,用于简化并发编程。

  • ExecutorService:用于执行异步任务的接口。它可以将任务提交给一个线程池执行。
  • Future:表示异步计算的结果。通过它,可以查询任务是否完成,或阻塞等待结果。
  • CountDownLatch:一个计数器,用于等待所有操作完成。计数器减到0时,所有等待的线程可以继续执行。
  • Semaphore:一个计数信号量。可以用于限制同时访问的资源数量。

示例代码:

import java.util.concurrent.*;

public class ConcurrentExample {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        Future<String> future = executor.submit(() -> {
            return "Task completed";
        });
        System.out.println(future.get());
        executor.shutdown();
    }
}

Java线程管理

创建和启动线程

在Java中,可以使用Thread类或者实现Runnable接口来创建和启动线程。

  • 使用Thread类:直接继承Thread类,重写run()方法。
  • 使用Runnable接口:实现Runnable接口,重写run()方法,然后通过Thread类的构造方法启动线程。

示例代码:

public class ThreadExample {

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread1 running");
        });

        Runnable task = () -> {
            System.out.println("Thread2 running");
        };

        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();
    }
}

线程生命周期

Java线程的生命周期包括以下几种状态:

  • 新建状态(New):创建Thread对象后,线程处于新建状态。
  • 可运行状态(Runnable):线程调度器将线程放入可运行队列中,等待执行。
  • 运行状态(Running):线程正在执行run()方法中的代码。
  • 阻塞状态(Blocked):线程被阻塞,等待资源或条件。
  • 等待状态(Waiting):线程调用wait()方法进入等待状态。
  • 定时等待状态(Timed Waiting):线程调用sleep()wait()join()等方法进入定时等待状态。
  • 终止状态(Terminated):线程执行完毕,进入终止状态。

线程同步与互斥

线程同步用于确保多个线程访问共享资源时不会出现数据不一致的问题。线程互斥则确保同一时间内只有一个线程可以访问共享资源。

示例代码:

public class SyncExample {

    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }

    public synchronized int getCount() {
        return count;
    }
}

public class ComplexSyncExample {

    private int sharedValue = 0;

    public synchronized void increment() {
        sharedValue++;
    }

    public synchronized void decrement() {
        sharedValue--;
    }

    public synchronized int getValue() {
        return sharedValue;
    }
}

线程间的通信

Java提供了wait()notify()notifyAll()方法,用于线程间的通信。

  • wait():使当前线程等待,直到其他线程调用notify()notifyAll()方法。
  • notify():唤醒一个等待的线程。
  • notifyAll():唤醒所有等待的线程。

示例代码:

public class ThreadCommunicationExample {

    private int count = 0;

    public synchronized void increment() {
        count++;
        System.out.println("Incremented to: " + count);
        notify(); // 唤醒等待的线程
    }

    public synchronized void waitForIncrement() {
        while (count < 1) {
            try {
                wait(); // 使当前线程等待,直到其他线程调用notify()或notifyAll()
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadCommunicationExample example = new ThreadCommunicationExample();
        Thread thread1 = new Thread(() -> example.increment());
        Thread thread2 = new Thread(() -> example.waitForIncrement());

        thread1.start();
        thread2.start();
    }
}

常用并发模式与设计模式

生产者消费者模式

生产者消费者模式是一种常见的并发模式,用于解决生产者生成数据,消费者消费数据的问题。通过使用队列作为缓冲区,可以实现线程之间的同步与解耦。

示例代码:

import java.util.concurrent.ArrayBlockingQueue;

public class ProducerConsumerExample {

    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    public class Producer extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 20; i++) {
                    queue.put(i);
                    System.out.println("Produced: " + i);
                    Thread.sleep(1000); // 模拟生产数据的时间
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public class Consumer extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 20; i++) {
                    int value = queue.take();
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ProducerConsumerExample example = new ProducerConsumerExample();
        Producer producer = example.new Producer();
        Consumer consumer = example.new Consumer();

        producer.start();
        consumer.start();
    }
}

读写器-写者问题

读写器-写者问题是一种经典的并发问题,涉及多个线程同时访问共享资源。读写器-写者问题的基本规则包括:

  • 写者优先:如果有写者等待,则所有读者和写者都必须等待,直到当前写者完成。
  • 读者优先:如果有读者等待,则所有写者必须等待,直到当前读者完成。
  • 读者互斥:如果一个读者正在读取资源,则不允许其他读者和写者访问资源。
  • 写者互斥:如果一个写者正在写入资源,则不允许其他写者和读者访问资源。

示例代码:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReaderWriterExample {

    private String data = "";
    private int readerCount = 0;
    private Lock readLock = new ReentrantLock();
    private Lock writeLock = new ReentrantLock();

    public void reader() {
        readLock.lock();
        readerCount++;
        readLock.unlock();

        writeLock.lock();
        try {
            System.out.println("Reader is reading: " + data);
        } finally {
            writeLock.unlock();
        }

        readLock.lock();
        readerCount--;
        if (readerCount == 0) {
            readLock.unlock();
        }
    }

    public void writer(String newData) {
        writeLock.lock();
        try {
            System.out.println("Writer is writing: " + newData);
            data = newData;
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        ReaderWriterExample example = new ReaderWriterExample();

        Thread reader1 = new Thread(() -> example.reader());
        Thread reader2 = new Thread(() -> example.reader());
        Thread writer = new Thread(() -> example.writer("New Data"));

        reader1.start();
        reader2.start();
        writer.start();
    }
}

单例模式的线程安全实现

单例模式是一种常用的软件设计模式,确保一个类只有一个实例,并提供一个全局访问点。为了确保单例模式在多线程环境下线程安全,可以使用synchronized关键字或者双重检查锁机制。

示例代码:

public class Singleton {

    private static volatile Singleton instance;

    private Singleton() {
        // 私有构造函数
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

实战演练:构建简单的高并发系统

分布式锁的实现

分布式锁是一种在分布式系统中实现互斥访问的方式,通常用于解决多个节点对同一资源的竞争问题。常用的分布式锁实现方式包括基于Redis、Zookeeper等中间件。

示例代码(基于Redis实现):

import redis.clients.jedis.Jedis;

public class DistributedLockExample {

    private Jedis jedis = new Jedis("localhost");

    public boolean lock(String key, int expireTime) {
        if (jedis.setnx(key, "1") == 1) {
            jedis.expire(key, expireTime);
            return true;
        }
        return false;
    }

    public void unlock(String key) {
        jedis.del(key);
    }

    public static void main(String[] args) {
        DistributedLockExample example = new DistributedLockExample();

        Thread thread1 = new Thread(() -> {
            if (example.lock("lock1", 10)) {
                try {
                    System.out.println("Locked by thread1");
                    Thread.sleep(5000); // 模拟业务逻辑
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    example.unlock("lock1");
                    System.out.println("Unlocked by thread1");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            if (example.lock("lock1", 10)) {
                try {
                    System.out.println("Locked by thread2");
                    Thread.sleep(5000); // 模拟业务逻辑
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    example.unlock("lock1");
                    System.out.println("Unlocked by thread2");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

使用Semaphore控制并发数

Semaphore是一个计数信号量,可以用于限制同时访问的资源数量。通过Semaphore,可以在多线程环境中控制并发数,避免资源过载。

示例代码:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreExample {

    private Semaphore semaphore = new Semaphore(5); // 最大并发数为5

    public void acquire() throws InterruptedException {
        semaphore.acquire(); // 获取信号量
        try {
            System.out.println("Acquired semaphore");
            Thread.sleep(1000); // 模拟业务逻辑
        } finally {
            semaphore.release(); // 释放信号量
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SemaphoreExample example = new SemaphoreExample();

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                try {
                    example.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }

        TimeUnit.SECONDS.sleep(10); // 等待所有线程执行完毕
        for (Thread thread : threads) {
            thread.join();
        }
    }
}

异步处理与回调函数

异步处理是一种编程模式,用于提高系统响应速度和资源利用率。通过异步处理,可以在等待某个操作的期间执行其他任务。回调函数是一种处理异步操作的结果的方式。

示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class AsyncExample {

    public void asyncTask(Runnable task, Runnable callback) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<?> future = executor.submit(task);
        future.addListener(() -> {
            callback.run();
        }, executor);
        executor.shutdown();
    }

    public static void main(String[] args) {
        AsyncExample example = new AsyncExample();

        example.asyncTask(() -> {
            System.out.println("Task is running");
            Thread.sleep(1000); // 模拟业务逻辑
        }, () -> {
            System.out.println("Task completed");
        });
    }
}

调试与性能优化

日志与调试技巧

日志是调试和维护的重要工具。Java中有多种日志库可供选择,如Log4j、SLF4J等。通过配置日志级别和输出格式,可以更方便地进行调试。

示例代码(使用Log4j):

import org.apache.log4j.Logger;

public class LoggingExample {

    private static final Logger logger = Logger.getLogger(LoggingExample.class);

    public void logInfo() {
        logger.info("This is an info log");
    }

    public void logError() {
        logger.error("This is an error log");
    }

    public static void main(String[] args) {
        LoggingExample example = new LoggingExample();
        example.logInfo();
        example.logError();
    }
}

JVisualVM性能分析工具

JVisualVM是Java自带的性能分析工具,可以用于监控和分析应用程序的性能。它提供了堆转储、线程转储、CPU使用率、内存使用情况等功能。

使用步骤:

  1. 打开JVisualVM工具。
  2. 通过“文件”菜单或工具栏的按钮连接到目标应用程序的JVM进程。
  3. 使用“监视”选项卡查看CPU使用率、内存使用情况等。
  4. 使用“线程”选项卡查看线程的详细信息,包括线程状态、调用栈等。
  5. 使用“堆转储”功能生成堆转储文件,分析对象的分配和引用情况。

避免死锁和内存泄漏

死锁是指多个线程互相等待对方释放资源,导致所有线程都无法继续执行。内存泄漏是指程序分配的内存不能被回收而一直占用,导致系统资源逐渐耗尽。

避免死锁的方法:

  • 使用synchronized块而不是synchronized方法,以减少死锁的机会。
  • 确保资源获取顺序一致,避免循环等待。
  • 使用超时机制,定期检查线程状态并释放资源。

示例代码:

public class AvoidDeadlockExample {

    private Object resource1 = new Object();
    private Object resource2 = new Object();

    public void method1() {
        synchronized (resource1) {
            System.out.println("Acquired resource1");
            Thread.yield(); // 模拟线程调度
            synchronized (resource2) {
                System.out.println("Acquired resource2");
            }
        }
    }

    public void method2() {
        synchronized (resource2) {
            System.out.println("Acquired resource2");
            Thread.yield(); // 模拟线程调度
            synchronized (resource1) {
                System.out.println("Acquired resource1");
            }
        }
    }

    public static void main(String[] args) {
        AvoidDeadlockExample example = new AvoidDeadlockExample();
        Thread thread1 = new Thread(() -> example.method1());
        Thread thread2 = new Thread(() -> example.method2());

        thread1.start();
        thread2.start();
    }
}

public class AvoidComplexDeadlockExample {

    private Object resource1 = new Object();
    private Object resource2 = new Object();

    public void method1() {
        synchronized (resource1) {
            System.out.println("Acquired resource1");
            try {
                Thread.sleep(1000); // 模拟业务逻辑
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (resource2) {
                System.out.println("Acquired resource2");
            }
        }
    }

    public void method2() {
        synchronized (resource2) {
            System.out.println("Acquired resource2");
            try {
                Thread.sleep(1000); // 模拟业务逻辑
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (resource1) {
                System.out.println("Acquired resource1");
            }
        }
    }

    public static void main(String[] args) {
        AvoidComplexDeadlockExample example = new AvoidComplexDeadlockExample();
        Thread thread1 = new Thread(() -> example.method1());
        Thread thread2 = new Thread(() -> example.method2());

        thread1.start();
        thread2.start();
    }
}

避免内存泄漏的方法:

  • 避免静态集合存储临时对象,使用适当的生命周期管理。
  • 避免持有已失效对象的引用,确保垃圾回收机制可以回收不再使用的对象。
  • 及时清理不再使用的资源,如文件流、数据库连接等。

示例代码:

public class AvoidMemoryLeakExample {

    private static Map<String, String> map = new HashMap<>();

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                Thread.sleep(1000); // 模拟长时间运行的任务
                map.put("key", "value");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 清理不再使用的资源
        map.clear();
        System.gc(); // 强制垃圾回收
    }
}

public class AvoidComplexMemoryLeakExample {

    private static Map<String, String> map = new HashMap<>();

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                Thread.sleep(1000); // 模拟长时间运行的任务
                map.put("key", "value");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 清理不再使用的资源
        map.clear();
        System.gc(); // 强制垃圾回收
        System.runFinalization(); // 执行终结操作
    }
}
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消