Java2万个整数数组求和

Java2万个整数数组求和

需求如下

用多线程对2万个整数的数组计算和,并得到结果。

具体题目

计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。

常见问题

用下面博主写的没有方法的代码运行就会发现问题。运行多次后,会发现最终得到的结果不一样,并且不正确。这就是多线程互相争抢资源的结果,那么我们怎么来把这个问题简单的解决一下了。下面就是博主对这个问题的解答过程。

多线程的创建方式:两种方法

Runnable接口

我们通过三种方法(睡眠、合并、判断)来解决线程间抢资源的问题,会发现,
睡眠:Thread.sleep(1000(1秒)//延迟的时间):这样子线程就有足够的时间来完成运算。缺点:对于时间的掌握不准确,有可能浪费时间或者时间不够。

public class test {
	public static void main(String[] args) throws InterruptedException {
		/**
		 * 计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。
		 */
		int[] cs = new int[20000];
		for (int i = 0; i < cs.length; i++) {
			cs[i] = i+1;//为什么加1:因为i是下标,起始值为0,我们输入的值从1开始,所有加1
		}
		//创建实现类对象		
		Tesk tesk1 = new Tesk(1,5000,cs);
		Tesk tesk2 = new Tesk(5001,10000,cs);
		Tesk tesk3 = new Tesk(10001,15000,cs);
		Tesk tesk4 = new Tesk(15001,20000,cs);
		//创建子线程对象		
		Thread t1 = new Thread(tesk1);
		Thread t2 = new Thread(tesk2);
		Thread t3 = new Thread(tesk3);
		Thread t4 = new Thread(tesk4);
		//线程启动:开始运行	
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		//方式一:睡眠:给子线程足够的时间计算,但是浪费时间。
		Thread.sleep(1000);	//延迟1000毫秒
		
		System.out.println(tesk1.getsum()+tesk2.getsum()+tesk3.getsum()+tesk4.getsum());
		}
}
class Tesk implements Runnable{	
	 private int startnum;//开始数
	 private int endnum;//结尾数
	 private int sum;//和
	 private int[] is;//数组
	// private boolean flag = true;

	public Tesk(int startnum, int endnum, int[] is) {
		this.startnum = startnum;
		this.endnum = endnum;
		this.is = is;
	}
	//重写Runnable中的run方法
	@Override
	public void run() {
		for (int i = startnum; i <= endnum; i++) {
			this.sum +=is[i-1];			
		}//flag = false;		
	}
	//因为是私有属性,所有通过一个方法来返回sum(计算和的结果)
	public int  getsum() {//返回出各线程的计算和值
		return this.sum;
	}	
}

判断:通过在MyThread类里面加flag(来判断),在主函数里通过while(这里就有朋友问了,为啥不用if来做判断,if条件是不满足时也会往下运行,而while只会一直循环,直到满足条件后,才会执行下面的代码)来判断子线程是否运行完成。完成就执行主函数的代码。

public class test {
   public static void main(String[] args) throws InterruptedException {
   	int[] cs = new int[20000];
   	for (int i = 0; i < cs.length; i++) {
   		cs[i] = i+1;//为什么加1:因为i是下标,起始值为0,我们输入的值从1开始,所有加1
   	}
   	//创建实现类对象		
   	Tesk tesk1 = new Tesk(1,5000,cs);
   	Tesk tesk2 = new Tesk(5001,10000,cs);
   	Tesk tesk3 = new Tesk(10001,15000,cs);
   	Tesk tesk4 = new Tesk(15001,20000,cs);
   	//创建子线程对象		
   	Thread t1 = new Thread(tesk1);
   	Thread t2 = new Thread(tesk2);
   	Thread t3 = new Thread(tesk3);
   	Thread t4 = new Thread(tesk4);
   	//线程启动:开始运行	
   	t1.start();
   	t2.start();
   	t3.start();
   	t4.start();	
   	//方法二:
   	while (tesk1.isflag() || tesk2.isflag() || tesk3.isflag() || tesk4.isflag()) {//当每一个线程都运行完时,就会返回
    }
   	
   	System.out.println(tesk1.getsum()+tesk2.getsum()+tesk3.getsum()+tesk4.getsum());
   	}
}
class Tesk implements Runnable{	
    private int startnum;//开始数
    private int endnum;//结尾数
    private int sum;//和
    private int[] is;//数组
    private boolean flag = true;//判断,现在状态为true

   public Tesk(int startnum, int endnum, int[] is) {
   	this.startnum = startnum;
   	this.endnum = endnum;
   	this.is = is;
   }
   //重写Runnable中的run方法
   @Override
   public void run() {
   	for (int i = startnum; i <= endnum; i++) {
   		this.sum +=is[i-1];			
   	}flag = false;	//运行完后就给flag赋值false;	
   }
   //因为是私有属性,所有通过一个方法来返回sum(计算和的结果)
   public int  getsum() {//返回出各线程的计算和值
   	return this.sum;
   }	
   public boolean isflag() {//返回flag的状态
   	return flag;
   }	
}

合并:(推荐)大概的意思就是把我们想要先执行的代码,嵌入到主函数中,这样主函数执行时,就会先执行我们插入的代码块。这种也不会浪费时间,也不需要做多余的代码操作。博主推荐新手可以用这种方法,比较好理解代码量也比较少。

public class test {
	public static void main(String[] args) throws InterruptedException {
		int[] cs = new int[20000];
		for (int i = 0; i < cs.length; i++) {
			cs[i] = i+1;//为什么加1:因为i是下标,起始值为0,我们输入的值从1开始,所有加1
		}
		//创建实现类对象		
		Tesk tesk1 = new Tesk(1,5000,cs);
		Tesk tesk2 = new Tesk(5001,10000,cs);
		Tesk tesk3 = new Tesk(10001,15000,cs);
		Tesk tesk4 = new Tesk(15001,20000,cs);
		//创建子线程对象		
		Thread t1 = new Thread(tesk1);
		Thread t2 = new Thread(tesk2);
		Thread t3 = new Thread(tesk3);
		Thread t4 = new Thread(tesk4);
		//线程启动:开始运行	
		t1.start();
		t2.start();
		t3.start();
		t4.start();	
		//方法三:合并join方法为合并
		t1.join();
		t2.join();
		t3.join();
		t4.join();		
		System.out.println(tesk1.getsum()+tesk2.getsum()+tesk3.getsum()+tesk4.getsum());
		}
}
class Tesk implements Runnable{	
	 private int startnum;//开始数
	 private int endnum;//结尾数
	 private int sum;//和
	 private int[] is;//数组
	 
	public Tesk(int startnum, int endnum, int[] is) {
		this.startnum = startnum;
		this.endnum = endnum;
		this.is = is;
	}
	//重写Runnable中的run方法
	@Override
	public void run() {
		for (int i = startnum; i <= endnum; i++) {
			this.sum +=is[i-1];			
		}flag = false;	//运行完后就给flag赋值false;	
	}
	//因为是私有属性,所有通过一个方法来返回sum(计算和的结果)
	public int  getsum() {//返回出各线程的计算和值
		return this.sum;
	}		
}

Thread类

此方法也是和上面的方法也是一样,只是不用实现接口了,直接在主函数里面new 对象,那么在这简单描述一下。

public class Work {
	
	public static void main(String[] args) throws InterruptedException {
				
		//声明包含2万个整数的数组
		int[] is = new int[20000];
		//初始化数据
		for (int i = 0; i < is.length; i++) {
			is[i] = (i+1);
		}
		
		MyThread t1 = new MyThread(0, 4999, is);
		MyThread t2 = new MyThread(5000, 9999, is);
		MyThread t3 = new MyThread(10000, 14999, is);
		MyThread t4 = new MyThread(15000, 19999, is);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
		//数据错误的原因:4个子线程还没有执行完毕,主线程就抢到资源并输出结果
		//解决方案:4个子线程全部执行完毕,再让主线程抢到资源
		
		//1.休眠
//		Thread.sleep(10);
		
		//2.获取线程状态
//		while(t1.isFlag() || t2.isFlag() || t3.isFlag() || t4.isFlag()){
//		}
		
		//3.合并
		t1.join();
		t2.join();
		t3.join();
		t4.join();		
		
		System.out.println(t1.getSum() + t2.getSum() + t3.getSum() + t4.getSum());
	}
}
class MyThread extends Thread{
	
	private int startIndex;
	private int endIndex;
	private int[] is;
	private int sum;
	private boolean flag = true;
	
	public MyThread(int startIndex, int endIndex, int[] is) {
		this.startIndex = startIndex;
		this.endIndex = endIndex;
		this.is = is;
	}

	@Override
	public void run() {
		for (int i = startIndex; i <= endIndex; i++) {
			sum += is[i];
		}
		flag = false;
	}

	public int getSum() {
		return sum;
	}

	public boolean isFlag() {
		return flag;
	}
}