MST

星途 面试题库

面试题:Java中StringTokenizer处理复杂分隔符字符串在多线程环境下的问题及解决方案

在多线程环境中,使用StringTokenizer处理复杂分隔符字符串可能会出现哪些问题?请详细分析并提供至少一种可靠的线程安全解决方案,包括对解决方案原理的阐述和相应的代码实现。
41.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

问题分析

  1. 线程安全问题StringTokenizer不是线程安全的。在多线程环境下,多个线程同时调用StringTokenizer的方法(如nextToken())可能会导致数据不一致。因为StringTokenizer内部维护了当前解析位置等状态信息,多个线程并发访问和修改这些状态可能造成混乱。
  2. 数据竞争:例如,一个线程可能在另一个线程尚未完全处理完当前标记时就改变了字符串的解析位置,导致错误的标记被返回或解析过程混乱。

线程安全解决方案

  1. 使用同步块(Synchronized Block)
    • 原理:通过synchronized关键字,将对StringTokenizer的操作包裹在同步块内,使得同一时间只有一个线程能够进入该同步块,操作StringTokenizer,从而避免数据竞争。
    • 代码实现
import java.util.StringTokenizer;

public class ThreadSafeStringTokenizer {
    private final StringTokenizer tokenizer;

    public ThreadSafeStringTokenizer(String str, String delim) {
        this.tokenizer = new StringTokenizer(str, delim);
    }

    public synchronized String nextToken() {
        if (tokenizer.hasMoreTokens()) {
            return tokenizer.nextToken();
        }
        return null;
    }
}

使用示例:

public class Main {
    public static void main(String[] args) {
        ThreadSafeStringTokenizer tst = new ThreadSafeStringTokenizer("a,b;c", ",;");
        String token;
        while ((token = tst.nextToken()) != null) {
            System.out.println(token);
        }
    }
}
  1. 使用ThreadLocal
    • 原理ThreadLocal为每个线程提供独立的变量副本。在这里,可以将StringTokenizer作为ThreadLocal的变量,每个线程都有自己的StringTokenizer实例,避免了多线程对同一实例的竞争。
    • 代码实现
import java.util.StringTokenizer;

public class ThreadLocalStringTokenizer {
    private static final ThreadLocal<StringTokenizer> threadLocalTokenizer = ThreadLocal.withInitial(() -> null);

    public static void setTokenizer(String str, String delim) {
        threadLocalTokenizer.set(new StringTokenizer(str, delim));
    }

    public static String nextToken() {
        StringTokenizer tokenizer = threadLocalTokenizer.get();
        if (tokenizer != null && tokenizer.hasMoreTokens()) {
            return tokenizer.nextToken();
        }
        return null;
    }
}

使用示例:

public class Main {
    public static void main(String[] args) {
        ThreadLocalStringTokenizer.setTokenizer("a,b;c", ",;");
        String token;
        while ((token = ThreadLocalStringTokenizer.nextToken()) != null) {
            System.out.println(token);
        }
    }
}