Java多线程自增程序为何抛出异常?线程为何按顺序执行?
异常抛出原因
你的主类中第一个生成线程的for循环存在逻辑错误:循环头已经定义了每次循环结束执行i++,你又在循环体中额外加了一行i++,相当于每轮循环i的值会增加2,循环只会执行25次,arrayOfThreads里最终只有25个线程对象。
而后续启动线程的for循环会遍历下标0到49,当访问下标25的元素时,就会抛出IndexOutOfBoundsException数组越界异常。
线程看似按顺序执行的原因
你每个线程的run方法执行逻辑极轻量,仅包含一行打印和一次变量自增,CPU可以在几纳秒内执行完成。你是在循环中依次调用线程的start()方法,可能前一个线程启动后瞬间就执行完了所有逻辑,下一个线程才被启动,所以观测到的执行顺序看起来和启动顺序一致。
如果要观测到乱序执行的效果,可以在run方法的逻辑中加入一段随机时长的短休眠,示例如下:
@Override public void run() { try { // 加入随机休眠,放大线程调度的差异 Thread.sleep(new Random().nextInt(10)); System.out.println( this.getName() + " : Sum is " + sum ); ++sum; } catch (Exception e) { e.printStackTrace(); } }
额外注意:你的sum变量并未实现多线程共享
你当前是让任务类继承Thread,每次创建线程都新建一个AddOneToSum实例,每个实例都持有自己私有的sum变量,完全没有共享,所以最终达不到多线程累加同一个sum的预期效果。
如果要实现多线程共同累加同一个变量,有两种常见方案:
- 将
sum变量改为static静态变量,属于类全局共享 - 改为实现
Runnable接口,将同一个Runnable实例传给所有线程对象
同时要注意多线程累加操作不是原子操作,需要加锁或者使用java.util.concurrent.atomic.AtomicInteger来保证线程安全,避免出现计数错误。
最简修复代码
修复后的任务类
import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; public class AddOneToSum extends Thread { // 改为静态共享,用原子类保证线程安全 private static AtomicInteger sum = new AtomicInteger(0); @Override public void run() { try { Thread.sleep(new Random().nextInt(10)); System.out.println( this.getName() + " : Sum is " + sum.getAndIncrement() ); } catch (Exception e) { e.printStackTrace(); } } }
修复后的主类
import java.util.*; public class Assignment9 { public static void main(String[] args) { ArrayList<Thread> arrayOfThreads = new ArrayList<Thread>(); try { for ( int i = 0 ; i < 50 ; i++ ) { arrayOfThreads.add( new AddOneToSum() ); // 删掉多余的i++ } for ( int i = 0 ; i < 50 ; i++ ) { arrayOfThreads.get(i).start(); } } catch (Exception e) { e.printStackTrace(); } } }
内容的提问来源于stack exchange,提问作者SebastianAltomare
相关产品推荐
相关产品推荐

