Java学习教程,从入门到精通,Java ConcurrentHashMap语法知识点及案例代码(63)

Java ConcurrentHashMap语法知识点及案例代码

ConcurrentHashMap 是 Java 中一个非常重要的线程安全的哈希表实现,它允许并发访问和修改,并且性能相对较好。以下是 ConcurrentHashMap 的语法知识点以及一个带有详细注释的示例代码。

语法知识点

  1. 引入包

    java 复制代码
    import java.util.concurrent.ConcurrentHashMap;
  2. 创建对象

    java 复制代码
    ConcurrentHashMap<KeyType, ValueType> map = new ConcurrentHashMap<>();
  3. 基本方法

    • put(K key, V value):将一个键值对放入映射中。
    • get(Object key):根据键获取值。
    • remove(Object key):根据键移除键值对。
    • containsKey(Object key):检查映射中是否包含指定的键。
    • size():返回映射中的键值对数量。
    • isEmpty():检查映射是否为空。
    • forEach(BiConsumer<? super K, ? super V> action):对映射中的每个键值对执行给定的操作。
  4. 并发特性

    • ConcurrentHashMap 使用分段锁(Segment Locks)或更现代的 CAS(Compare-And-Swap)操作来实现并发性,从而允许多个线程同时读取和写入。
    • 它的内部实现复杂,但对外提供了简洁的接口。

示例代码

以下是一个简单的示例代码,展示了如何使用 ConcurrentHashMap 来存储和管理线程安全的键值对。

java 复制代码
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class ConcurrentHashMapExample {

    public static void main(String[] args) {
        // 创建一个ConcurrentHashMap实例
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        // 使用put方法添加元素
        map.put("Apple", 1);
        map.put("Banana", 2);
        map.put("Orange", 3);

        // 使用get方法获取元素
        Integer value = map.get("Banana");
        System.out.println("Value for 'Banana': " + value);

        // 使用containsKey方法检查是否包含某个键
        boolean containsKey = map.containsKey("Apple");
        System.out.println("Map contains 'Apple': " + containsKey);

        // 使用remove方法移除元素
        map.remove("Orange");

        // 使用size方法获取元素数量
        int size = map.size();
        System.out.println("Size of the map: " + size);

        // 使用isEmpty方法检查是否为空
        boolean isEmpty = map.isEmpty();
        System.out.println("Is the map empty? " + isEmpty);

        // 使用forEach方法遍历所有元素
        System.out.println("Elements in the map:");
        map.forEach((key, value) -> {
            System.out.println("Key: " + key + ", Value: " + value);
        });

        // 模拟并发访问
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                String key = "Key" + i;
                map.put(key, i);
                System.out.println(Thread.currentThread().getName() + " added " + key + " with value " + i);
            }
        };

        // 创建并启动多个线程
        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

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

        try {
            // 等待线程结束
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 最终的元素数量(可能大于初始数量,因为多线程添加)
        System.out.println("Final size of the map after concurrent operations: " + map.size());
    }
}

代码解释

  1. 创建 ConcurrentHashMap 实例

    java 复制代码
    ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
  2. 添加元素

    java 复制代码
    map.put("Apple", 1);
    map.put("Banana", 2);
    map.put("Orange", 3);
  3. 获取元素

    java 复制代码
    Integer value = map.get("Banana");
  4. 检查是否包含某个键

    java 复制代码
    boolean containsKey = map.containsKey("Apple");
  5. 移除元素

    java 复制代码
    map.remove("Orange");
  6. 获取元素数量

    java 复制代码
    int size = map.size();
  7. 检查是否为空

    java 复制代码
    boolean isEmpty = map.isEmpty();
  8. 遍历所有元素

    java 复制代码
    map.forEach((key, value) -> {
        System.out.println("Key: " + key + ", Value: " + value);
    });
  9. 模拟并发访问

    • 定义了一个 Runnable 任务,每个任务会向 ConcurrentHashMap 中添加一些键值对。
    • 创建并启动两个线程来执行这个任务。
    • 使用 join 方法等待两个线程执行完毕。
  10. 输出最终的元素数量

    java 复制代码
    System.out.println("Final size of the map after concurrent operations: " + map.size());

通过这些示例和解释,你应该能够掌握 ConcurrentHashMap 的基本用法和并发特性。希望这些信息对你有帮助!

以下是关于 ConcurrentHashMap 的几个具体案例,这些案例展示了在不同场景下如何使用 ConcurrentHashMap 来实现线程安全的键值对存储和访问。

案例一:用户访问计数器

在这个案例中,我们使用 ConcurrentHashMap 来实现一个用户访问计数器,用于统计不同用户的访问次数。

java 复制代码
import java.util.concurrent.ConcurrentHashMap;

public class UserVisitCounter {
    // 使用ConcurrentHashMap存储用户访问次数
    private ConcurrentHashMap<String, Integer> userCounts = new ConcurrentHashMap<>();

    // 用户访问时调用此方法增加计数
    public void visit(String userId) {
        // 使用compute方法实现线程安全的计数更新
        userCounts.compute(userId, (key, value) -> (value == null) ? 1 : value + 1);
    }

    // 获取用户的访问次数
    public int getCount(String userId) {
        // 使用getOrDefault方法获取访问次数,如果未找到则返回0
        return userCounts.getOrDefault(userId, 0);
    }

    public static void main(String[] args) {
        UserVisitCounter counter = new UserVisitCounter();

        // 模拟多线程用户访问
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                String userId = "User" + (int) (Math.random() * 100);
                counter.visit(userId);
                System.out.println(Thread.currentThread().getName() + " visited " + userId + ", total count: " + counter.getCount(userId));
            }
        };

        // 创建并启动多个线程
        for (int i = 0; i < 5; i++) {
            new Thread(task).start();
        }
    }
}

案例二:缓存系统

在这个案例中,我们使用 ConcurrentHashMap 来实现一个简单的缓存系统,用于存储和访问缓存数据。

java 复制代码
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class SimpleCache<K, V> {
    // 使用ConcurrentHashMap存储缓存数据
    private ConcurrentHashMap<K, CacheEntry<K, V>> cache = new ConcurrentHashMap<>();

    // 缓存项,包含值和过期时间
    private static class CacheEntry<K, V> {
        final V value;
        final long expireTime;

        CacheEntry(V value, long expireTime) {
            this.value = value;
            this.expireTime = expireTime;
        }

        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }

    // 添加缓存项,指定过期时间
    public void put(K key, V value, long expireTime, TimeUnit timeUnit) {
        long expireTimeMillis = System.currentTimeMillis() + timeUnit.toMillis(expireTime);
        cache.put(key, new CacheEntry<>(value, expireTimeMillis));
    }

    // 获取缓存项的值,如果缓存项已过期,则返回null
    public V get(K key) {
        CacheEntry<K, V> entry = cache.get(key);
        if (entry != null && !entry.isExpired()) {
            return entry.value;
        } else {
            // 缓存项已过期或不存在,从缓存中移除(可选)
            cache.remove(key);
            return null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SimpleCache<String, String> cache = new SimpleCache<>();

        // 添加缓存项,设置过期时间为5秒
        cache.put("key1", "value1", 5, TimeUnit.SECONDS);

        // 访问缓存项
        System.out.println("key1: " + cache.get("key1")); // 输出: value1

        // 等待6秒,使缓存项过期
        Thread.sleep(6000);

        // 再次访问缓存项,此时应返回null
        System.out.println("key1 after expiration: " + cache.get("key1")); // 输出: null
    }
}

案例三:任务分配系统

在这个案例中,我们使用 ConcurrentHashMap 来实现一个简单的任务分配系统,用于将任务分配给多个工作线程。

java 复制代码
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TaskDistributor {
    // 使用ConcurrentHashMap存储任务和对应的处理线程
    private ConcurrentHashMap<String, String> taskAssignments = new ConcurrentHashMap<>();

    // 添加任务并分配处理线程
    public void assignTask(String taskId, String workerId) {
        taskAssignments.put(taskId, workerId);
    }

    // 获取任务的处理线程
    public String getWorkerForTask(String taskId) {
        return taskAssignments.get(taskId);
    }

    public static void main(String[] args) {
        TaskDistributor distributor = new TaskDistributor();

        // 创建工作线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 模拟添加任务并分配处理线程
        for (int i = 0; i < 10; i++) {
            String taskId = "Task" + i;
            String workerId = "Worker" + (i % 3); // 简单地使用取模运算分配工作线程
            distributor.assignTask(taskId, workerId);
        }

        // 工作线程执行任务
        Runnable taskExecution = () -> {
            for (String taskId : distributor.taskAssignments.keySet()) {
                String workerId = distributor.getWorkerForTask(taskId);
                if (workerId.equals(Thread.currentThread().getName().substring(7))) {
                    System.out.println(Thread.currentThread().getName() + " is processing " + taskId);
                }
            }
        };

        // 启动工作线程
        for (int i = 0; i < 3; i++) {
            executorService.execute(new Thread(taskExecution, "Worker" + i));
        }

        // 关闭线程池(等待所有任务完成)
        executorService.shutdown();
    }
}

注意 :在第三个案例中,由于线程池和任务分配是并发进行的,因此实际的任务分配和处理可能会有所不同。这里的示例主要是为了展示如何使用 ConcurrentHashMap 来存储和访问任务分配信息。在实际应用中,可能需要更复杂的逻辑来处理任务分配和线程同步。

这些案例展示了 ConcurrentHashMap 在不同场景下的应用,包括用户访问计数器、缓存系统和任务分配系统。通过使用 ConcurrentHashMap,我们可以实现线程安全的键值对存储和访问,从而满足高并发场景下的需求。

相关推荐
杨超越luckly5 分钟前
利用高德API获取整个城市的公交路线并可视化(七)
大数据·算法·arcgis·信息可视化·数据挖掘·数据分析
7yewh5 分钟前
LeetCode 力扣 热题 100道(二十一)接雨水(C++)
开发语言·数据结构·c++·算法·leetcode·安卓
c++初学者ABC15 分钟前
统计字符的个数C++
开发语言·数据结构·c++
数据小小爬虫23 分钟前
如何利用Python爬虫获得1688商品详情
开发语言·爬虫·python
慧都小妮子30 分钟前
Spire.PDF for .NET【页面设置】演示:向 PDF 文档添加页码
java·pdf·.net
C666688834 分钟前
C#多线程
开发语言·汇编·c#
向宇it35 分钟前
【从零开始入门unity游戏开发之——C#篇16】C#什么是面向对象编程?
java·开发语言·vscode·unity·c#·游戏引擎
m0_6949380136 分钟前
Leetcode打卡:形成目标字符串需要的最少字符串数II
java·算法·leetcode
向宇it38 分钟前
【从零开始入门unity游戏开发之——C#篇17】C#面向对象的封装——类(Class)和对象、成员变量和访问修饰符、成员方法
java·开发语言·vscode·unity·c#·游戏引擎
坐井观老天39 分钟前
在C#中测试比较目录的不同方法以查看它们有哪些共同的文件
开发语言·c#