继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java高并发入门教程:轻松掌握并发编程基础

哆啦的时光机
关注TA
已关注
手记 201
粉丝 20
获赞 53
Java并发编程基础

并发编程在现代软件开发中越来越重要,随着硬件多核处理器的普及,充分利用多核处理器的能力可以显著提升程序的运行效率和响应速度。Java并发编程提供了丰富的API,使得编写高效的并发程序变得更加简单。掌握Java并发编程不仅可以提高程序效率,还能增强程序的可扩展性和稳定性。

Java并发编程的核心概念

  1. 线程(Thread):线程是程序执行的一个执行单元,Java中的每个线程都是一个Thread对象。
  2. 同步(Synchronization):确保多个线程在访问共享资源时不会出现数据不一致的问题。
  3. 并发容器(Concurrent Containers):专门用于多线程环境下的容器,提供线程安全的访问操作。
  4. 并发工具类:包括synchronized关键字、volatile关键字、Lock接口及Condition接口等。

Java并发编程的工具类介绍

Java并发工具类是Java并发编程的基础,掌握这些工具类是编写高效并发程序的关键。

  1. synchronized关键字:用于同步对象,确保同一时刻只有一个线程可以访问该对象。
  2. volatile关键字:确保变量的修改对所有线程可见,主要用于保证变量的可见性和多线程之间的通信。
  3. Lock接口和Condition接口:提供了比synchronized更灵活的同步机制,支持条件变量和更复杂的锁策略。

示例代码

public class SyncExample {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
            System.out.println("Incremented count to: " + count);
        }
    }

    public void decrement() {
        synchronized (lock) {
            count--;
            System.out.println("Decremented count to: " + count);
        }
    }
}
线程基础

创建和启动线程

Java中创建和启动线程的基本步骤如下:

  1. 创建一个实现Runnable接口的类或直接使用Thread类。
  2. Runnable实现类或Thread类中重写run方法。
  3. 创建线程对象并调用start方法启动线程。

示例代码:

public class ThreadExample {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 1: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread-1");
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 2: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread-2");

        t1.start();
        t2.start();
    }
}

线程的生命周期

Java线程有多种状态,包括:

  1. 新建状态:线程对象被创建,但还没有调用start方法。
  2. 可运行状态:线程被调度,等待CPU运行。
  3. 运行状态:线程正在执行run方法中的代码。
  4. 阻塞状态:线程被阻塞,等待某个事件发生,如等待I/O操作完成。
  5. 等待状态:调用wait方法,进入等待状态。
  6. 终止状态:线程已完成执行,或异常终止。

线程同步与互斥

线程同步和互斥是确保多线程环境下资源安全访问的关键机制。

线程同步

线程同步用于保证多个线程在访问共享资源时不会出现数据不一致的问题。Java提供了多种方式实现线程同步:

  1. synchronized关键字:用于方法或代码块实现同步。
  2. ReentrantLock类:提供了比synchronized更灵活的同步机制。

示例代码:

public class SyncExample {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
            System.out.println("Incremented count to: " + count);
        }
    }

    public void decrement() {
        synchronized (lock) {
            count--;
            System.out.println("Decremented count to: " + count);
        }
    }
}
常见并发问题及其解决方法

死锁

死锁是指两个或多个线程在等待对方释放资源,导致所有线程都无法继续执行的情况。避免死锁的方法包括:

  1. 避免资源循环等待:确保每个线程获取资源的顺序一致。
  2. 超时等待:使用tryLock方法尝试获取锁,避免长时间等待。
  3. 死锁检测与恢复:定期检测系统中的死锁,如果检测到死锁,强制释放资源。

示例代码:

public class DeadlockExample {
    private final Object resourceA = new Object();
    private final Object resourceB = new Object();

    public void methodA() {
        synchronized (resourceA) {
            synchronized (resourceB) {
                System.out.println("Method A");
            }
        }
    }

    public void methodB() {
        synchronized (resourceB) {
            synchronized (resourceA) {
                System.out.println("Method B");
            }
        }
    }
}

资源竞争

资源竞争是指多个线程同时访问共享资源导致数据不一致的问题。解决资源竞争的方法包括:

  1. 同步代码块:使用synchronized关键字或ReentrantLock类同步访问共享资源。
  2. 原子操作:使用原子变量AtomicInteger等类实现线程安全的原子操作。
  3. 并发容器:使用并发容器如ConcurrentHashMap等提供线程安全的访问。

示例代码:

public class ResourceCompetitionExample {
    private int count = 0;

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

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

    public int getCount() {
        return count;
    }
}

数据一致性

数据一致性是指多个线程访问共享资源时,数据的状态保持一致。保证数据一致性的方法包括:

  1. 使用synchronized关键字:确保同一时刻只有一个线程访问共享资源。
  2. 使用volatile关键字:确保变量的修改对所有线程可见。
  3. 使用并发容器:使用并发容器如ConcurrentHashMap等提供线程安全的访问。

示例代码:

public class DataConsistencyExample {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public void decrement() {
        count--;
    }

    public int getCount() {
        return count;
    }
}
Java并发工具类详解

synchronized关键字

synchronized关键字用于同步方法或代码块,确保同一时刻只有一个线程访问共享资源。synchronized关键字有两种使用方式:

  1. 同步方法:直接将方法声明为synchronized
  2. 同步代码块:将需要同步的代码块用synchronized关键字包裹。

示例代码:

public class SyncMethodExample {
    private int count = 0;

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

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

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

volatile关键字

volatile关键字用于保证变量的修改对所有线程可见,主要用于保证变量的可见性和多线程之间的通信。volatile变量的修改不会被编译器优化,确保每次读取到最新的值。

示例代码:

public class VolatileExample {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public boolean getFlag() {
        return flag;
    }
}

Lock接口和Condition接口

Lock接口提供了比synchronized更灵活的同步机制,支持条件变量和更复杂的锁策略。Condition接口用于实现条件变量,可以等待特定条件满足后再继续执行。

示例代码:

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

public class ReentrantLockExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean flag = false;

    public void setFlag() {
        lock.lock();
        try {
            flag = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void waitFlag() {
        lock.lock();
        try {
            while (!flag) {
                condition.await();
            }
            System.out.println("Flag is set");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
并发容器与集合类

并发容器的特点

并发容器是专门为多线程环境设计的容器,提供线程安全的访问操作。常见的并发容器包括ConcurrentHashMapConcurrentLinkedQueue等。

  1. ConcurrentHashMap:线程安全的哈希表,提供了比HashMap更好的并发性能。
  2. ConcurrentLinkedQueue:线程安全的链表队列,支持高效的并发访问。

使用场景及示例代码

ConcurrentHashMap

ConcurrentHashMap适用于高并发读写场景,可以保证线程安全。

示例代码:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

    public void put(String key, String value) {
        map.put(key, value);
    }

    public String get(String key) {
        return map.get(key);
    }

    public void remove(String key) {
        map.remove(key);
    }
}

ConcurrentLinkedQueue

ConcurrentLinkedQueue适用于需要高效并发访问的队列场景。

示例代码:

import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentLinkedQueueExample {
    private final ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();

    public void add(int value) {
        queue.add(value);
    }

    public Integer poll() {
        return queue.poll();
    }
}

常用的并发集合类介绍

ConcurrentHashMap

ConcurrentHashMap是线程安全的哈希表,提供了高效并发访问。ConcurrentHashMap内部使用多个锁,每个锁只保护一部分数据,从而提高了并发性能。

ConcurrentLinkedQueue

ConcurrentLinkedQueue是线程安全的链表队列,支持高效的并发访问。ConcurrentLinkedQueue内部使用无锁算法,避免了锁的竞争。

ConcurrentSkipListMap

ConcurrentSkipListMap是线程安全的跳表实现,支持高效的并发访问。ConcurrentSkipListMap适用于需要有序访问的数据结构。

实战案例:设计一个简单的高并发服务

分析需求

假设我们需要设计一个简单的高并发服务,该服务需要支持高并发的读写操作,例如统计访问次数、记录日志等。服务需要保证线程安全,避免数据不一致的问题。

设计方案

  1. 使用并发容器:使用ConcurrentHashMap存储访问次数和日志信息。
  2. 线程同步:使用synchronized关键字或ReentrantLock类实现线程同步。
  3. 异步处理:使用线程池处理并发请求,提高系统响应速度。

代码实现与优化

统计访问次数

import java.util.concurrent.ConcurrentHashMap;

public class HighConcurrencyService {
    private final ConcurrentHashMap<String, Integer> accessCountMap = new ConcurrentHashMap<>();

    public void incrementAccessCount(String key) {
        accessCountMap.put(key, accessCountMap.getOrDefault(key, 0) + 1);
    }

    public int getAccessCount(String key) {
        return accessCountMap.getOrDefault(key, 0);
    }

    public void decrementAccessCount(String key) {
        Integer count = accessCountMap.get(key);
        if (count != null && count > 0) {
            accessCountMap.put(key, count - 1);
        }
    }
}

记录日志

import java.util.concurrent.ConcurrentLinkedQueue;

public class LogService {
    private final ConcurrentLinkedQueue<String> logQueue = new ConcurrentLinkedQueue<>();

    public void log(String message) {
        logQueue.add(message);
    }

    public String pollLog() {
        return logQueue.poll();
    }
}

使用线程池处理并发请求

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

public class HighConcurrencyServiceExample {
    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    public void handleRequest(Runnable requestHandler) {
        executor.submit(requestHandler);
    }

    public void shutdown() {
        executor.shutdown();
    }
}

完整示例代码

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HighConcurrencyServiceExample {
    private final HighConcurrencyService highConcurrencyService = new HighConcurrencyService();
    private final LogService logService = new LogService();
    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    public void handleRequest(Runnable requestHandler) {
        executor.submit(() -> {
            try {
                requestHandler.run();
            } finally {
                logService.log("Request handled");
            }
        });
    }

    public void shutdown() {
        executor.shutdown();
    }

    public static void main(String[] args) {
        HighConcurrencyServiceExample service = new HighConcurrencyServiceExample();
        for (int i = 0; i < 10; i++) {
            service.handleRequest(() -> {
                highConcurrencyService.incrementAccessCount("example");
                System.out.println("Access count: " + highConcurrencyService.getAccessCount("example"));
            });
        }
        service.shutdown();
    }
}

通过以上示例代码,我们可以看到如何设计一个简单的高并发服务,包括使用并发容器、线程同步和异步处理等技术,确保服务能够在高并发情况下稳定运行。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP