多线程编程的目标与挑战
1.串行、并发与并行
假设有三件事A、B、C需要完成,其中完成事情A需实际投入5分钟并等待10分钟,完成事情B需要实际投入2分钟并等待8分钟,完成事情C需实际投入10分钟但不需要等待。那有以下三种方式来完成这几件事:
串行:先做事情A,待其完成再做事情B,以此类推。这种方式共需投入1个人,35分钟。
并发:这种方式也可以只投入一个人,其先把A的准备活动做好(实际投入的五分钟),再把B的准备活动做好,最后完成C。这种方式只需要17分钟。
并行:这种方式需要三个人同时做A、B、C,共需15分钟。
从软件的角度来说,并发就是在一段时间内以交替的方式去完成多个任务,而并行计算以齐头并进的方式去完成一个任务。多线程编程的实质就是将任务的处理方式从串行改为并发,实现并发化,发挥并发的优势。如果一个任务可以由串行改为并发(或串行)我们就称之为可并发化的(或可并行化的)。
2.竞态
多线程编程经常遇到的一个问题就是对于同样的输入,程序的输出有时是正确的有时是错误的。这样一个计算结果正确性与时间有关的现象称为竞态。
如下举一个竞态实例,某系统为了便于跟踪其接收到的HTTP请求的处理,会为收到的每个HTTP请求分配一个唯一编号Request ID。一个Request ID生成器的源码示例如下:
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RequestIDGenerator{
private final static RequestIDGenerator INSTANCE = new RequestIDGenerator();
private final static short SEQ_UPPER_LIMIT = 999;
private short sequence = -1;
private RequestIDGenerator() {
}
public short nextSequence(){
if (sequence >= SEQ_UPPER_LIMIT)
{
sequence = 0;
}
else {
sequence++;
}
return sequence;
}
public String nextID()
{
SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");
String timestamp = sdf.format(new Date());
DecimalFormat df = new DecimalFormat("000");
short sequenceNo = nextSequence();
return "0049"+timestamp+df.format(sequenceNo);
}
public static RequestIDGenerator getInstance()
{
return INSTANCE;
}
}
一个可能产生竞态的模拟业务如下:
import java.util.Random;
import java.util.Scanner;
public class RaceConditionDemo {
public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
int numberofThreads = in.nextInt();
Thread[] workerThreads = new Thread[numberofThreads];
for (int i = 0; i < numberofThreads; i++)
{
workerThreads[i] = new WorkerThread(i,10);
}
for (Thread ct : workerThreads)
{
ct.start();
}
}
static class WorkerThread extends Thread{
private final int requestCount;
public WorkerThread(int id, int requestCount)
{
super("worker-"+id);
this.requestCount = requestCount;
}
@Override
public void run()
{
int i = requestCount;
String requestID;
RequestIDGenerator requestIDGenerator = RequestIDGenerator.getInstance();
try {
while (i-- > 0)
{
requestID = requestIDGenerator.nextID();

本文深入探讨Java多线程编程中的关键概念,包括串行、并发与并行的比较,以及竞态条件和解决方法。文章通过实例分析了竞态产生的原因,如读-改-写操作和检测而后行动操作,指出synchronized关键字在防止竞态中的作用。此外,还介绍了线程安全性、原子性、可见性、有序性,以及上下文切换的影响和线程的活性故障。最后,讨论了资源争用与调度策略,包括公平与非公平调度的权衡。
最低0.47元/天 解锁文章
426

被折叠的 条评论
为什么被折叠?



