2011-2-17 不安全的多线程

本文探讨了SimpleDateFormat在多线程环境下使用的并发问题,并提出了三种解决方案:每次使用时创建新的实例、通过同步方法确保线程安全及根据线程ID分配独立实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们的代码的里面经常有这样的例子,为了格式化日期(如:2011-02-17), 就需要一个SimpleDateFormat类,同时,为了让整个系统共享这个格式化类,我们把这个格式化类写到一个公共的Util类里面,代码如下:
Util 类:
package com.maneco.art;

import java.text.SimpleDateFormat;

public class Util {
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

}

Util类的使用者:
package com.maneco.art;

import java.text.ParseException;

public class MultiThread implements Runnable {

public static void main(String[] args) {
new Thread(new MultiThread()).start();
new Thread(new MultiThread()).start();
new Thread(new MultiThread()).start();
new Thread(new MultiThread()).start();
}

@Override
public void run() {
try {
for (int i = 0; i < 10000; i++) {
System.out.print(Util.sdf.parse("2000-01-05"));
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}

如上面的代码,就会产生下面的异常:
Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Long.parseLong(Unknown Source)
at java.lang.Long.parseLong(Unknown Source)
at java.text.DigitList.getLong(Unknown Source)
at java.text.DecimalFormat.parse(Unknown Source)
at java.text.SimpleDateFormat.subParse(Unknown Source)
at java.text.SimpleDateFormat.parse(Unknown Source)
at java.text.DateFormat.parse(Unknown Source)
at com.maneco.art.MultiThread.run(MultiThread.java:18)
at java.lang.Thread.run(Unknown Source)

原因在于:
如果是单线程,使用这个Util类是没有问题的;
如果是多线程的话,就会产生并发访问同一个实例化的SimpleDateFormat类。
JDK API中声明DateFormat类和SimpleDateFormat类都不是线程同步的,推荐对于每个线程创建一个实例。如果多线程访问的话,自己实现多线程同步;
1: 最简单的方法是在每次使用的时候,new一个新的DateFormat;效率稍许损失,不能达到系统共享同一个格式化类。最简单的编程模型。推荐。
2: 增加线程同步访问: 但是多线程的效率将损失,并且容易导致死锁。
private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
public static synchronized Date formatStr(String str) throws ParseException {
return Util.df.parse(str);
}

下面这种线程同步是没有意义的,因为仅仅拿到reference时是线程同步的,而不是使用时同步的。
private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
public static synchronized DateFormat getDateFomrater() {
return Util.df;
}

3: 如果对性能要求严格,可以封装一个类,在同步的代码里面对于每个线程根据线程Id去返回对应的实例类,
效率稍微损失,不会导致死锁,系统的格式化聚集在一个访问点:
private static final Map<Long, DateFormat> DateFormaterMap = new HashMap<Long, DateFormat>();
// return date formater by thread Id
public static DateFormat getDateFormaterByThread() {
Long tId = Thread.currentThread().getId();

if (Util.DateFormaterMap.containsKey(tId)) {
return Util.DateFormaterMap.get(tId);
} else {
// another strategy: use SimpleDateFormat's clone method with a default one
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Util.DateFormaterMap.put(tId, sdf);
return sdf;
}
}

建议在写单元测试的时候,增加一项多线程测试。
<think>好的,我需要回答关于Microsoft Visual Studio 2010多线程的问题。首先,我得回忆一下VS2010在多线程编程方面的支持。记得VS2010是较旧的版本,但当时已经支持C++的多线程开发,可能通过标准库或其他API。 接下来,用户可能需要了解如何在VS2010中创建和管理线程。C++11是在2011年发布的,VS2010可能只支持部分C++11特性,比如std::thread可能还完全支持。可能需要使用更早的API,比如Windows API中的CreateThread函数,或者微软的并发运行时库,如Parallel Patterns Library (PPL)。 另外,MFC(Microsoft Foundation Classes)在VS2010中可能也有多线程支持,比如CWinThread类。需要提到同的方法,让用户知道有多种选择。 还要注意线程同步机制,比如临界区、互斥量、事件、信号量等,这些在Windows API中都有对应的实现,比如CRITICAL_SECTION、Mutex、Event、Semaphore。同时,VS2010可能支持OpenMP,用于并行循环,这也是一种多线程方式。 需要提醒用户在使用多线程时要注意的问题,比如竞态条件、死锁,以及如何调试多线程程序。VS2010的调试器是否有线程窗口,可以查看活动线程,设置断点等。 可能还需要比较VS2010与其他版本在多线程支持上的差异,比如后续版本对C++11/14/17更好的支持,但用户现在问的是2010,所以重点放在那个版本的功能。 最后,结构要清晰,分点介绍同的多线程实现方法,同步机制,调试工具,以及注意事项。确保信息准确,因为VS2010可能有些功能与现代版本同,需要确认当时的支持情况。</think>Microsoft Visual Studio 2010 多线程开发指南 以下从**实现方式**、**同步机制**和**调试工具**三个方面介绍多线程开发的关键技术: --- ### 一、多线程实现方式 1. **Windows API 原生支持** 使用 `CreateThread` 函数创建线程: ```cpp HANDLE hThread = CreateThread( NULL, // 安全属性 0, // 栈大小 MyThreadFunction, // 线程函数 pParam, // 参数指针 0, // 创建标志 NULL // 线程ID ); ``` - 需包含 `<windows.h>` - 线程函数格式:`DWORD WINAPI MyThreadFunction(LPVOID lpParam)` 2. **MFC 封装类 `CWinThread`** 适用于 MFC 应用程序: ```cpp CWinThread* pThread = AfxBeginThread( MyControllingFunction, // 控制函数 pParam // 参数 ); ``` - 控制函数格式:`UINT MyControllingFunction(LPVOID pParam)` 3. **OpenMP 并行指令** 通过预处理指令实现并行循环(需开启OpenMP支持): ```cpp #pragma omp parallel for for(int i=0; i<100; i++){ // 并行执行的代码 } ``` 4. **并发运行时库 (ConcRT)** VS2010 引入的 Parallel Patterns Library (PPL): ```cpp #include <ppl.h> Concurrency::parallel_for(0, 100, [](int i){ // 并行任务 }); ``` --- ### 二、线程同步机制 1. **临界区 (Critical Section)** ```cpp CRITICAL_SECTION cs; InitializeCriticalSection(&cs); EnterCriticalSection(&cs); // 临界区代码 LeaveCriticalSection(&cs); DeleteCriticalSection(&cs); ``` 2. **互斥量 (Mutex)** ```cpp HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); WaitForSingleObject(hMutex, INFINITE); // 受保护代码 ReleaseMutex(hMutex); ``` 3. **事件对象 (Event)** ```cpp HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); SetEvent(hEvent); // 触发事件 WaitForSingleObject(hEvent, INFINITE); // 等待事件 ``` 4. **信号量 (Semaphore)** ```cpp HANDLE hSemaphore = CreateSemaphore(NULL, initialCount, maxCount, NULL); WaitForSingleObject(hSemaphore, INFINITE); ReleaseSemaphore(hSemaphore, 1, NULL); ``` --- ### 三、调试与分析工具 1. **线程窗口** - 通过 `Debug > Windows > Threads` 查看活动线程状态 - 查看调用堆栈、挂起/恢复线程 2. **并行堆栈视图** `Debug > Windows > Parallel Stacks` 显示多线程执行路径 3. **性能分析器** `Analyze > Launch Performance Wizard` 可检测线程争用和负载均衡 --- ### 四、注意事项 1. **避免竞态条件**:确保共享资源的原子访问 2. **防止死锁**:按固定顺序获取多个锁 3. **线程优先级管理**:慎用 `SetThreadPriority`,避免饥饿问题 4. **UI线程限制**:MFC/GUI 操作必须驻留在主线程 --- ### 五、版本对比 VS2010 的局限性: - 支持 C++11 `std::thread` - ConcRT 功能较后续版本有限 建议升级到新版VS以获得更完善的多线程开发支持(如C++17并行算法)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值