Java多线程编程实战指南学习(二)

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

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();
                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值