Java 多线程并发实验(volatile, synchronized用法)

以下所有样例输出,结果不唯一,与操作系统对线程的调度有关。

一、模拟龟兔赛跑

(题目编号7179)
利用多线程技术编写一个模拟龟兔赛跑的程序,要求如下:
(1)乌龟每次跑一个单位,兔子每次跑10个单位;
(2)每个线程运行时,判断是否达到终点,如果到达终点,给出提示信息,
未到终点则提示目前距离终点的距离,并判断是否领先;
(3)如果兔子领先,则显示“我跑得快,睡一觉”信息,并睡一会。

代码:

class Race implements Runnable {
	final int total = 100; // 赛道长度
	volatile boolean isOver = false; // 比赛结束标志
	volatile int disRabbit = 0; // 兔子已走距离
	volatile int disTortoise = 0; // 乌龟已走距离
	@Override
	public void run() {
		for(int time = 1; isOver != true; time++) {
			if(Thread.currentThread().getName().equals("兔子")) {
				disRabbit = time * 10;
				if(disRabbit >= total) {
					isOver = true;
					System.out.println("兔子获胜!");
					break;
				}
				System.out.println("兔子距终点" + (total - disRabbit) + "米");
				if(disRabbit > disTortoise) {
					try {
						System.out.println("我跑得快,睡一觉\n");
						Thread.sleep((long) (Math.random()*2)); 
						// get a random number [0,1]
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			else {
				disTortoise = time;
				if(disTortoise >= total) {
					isOver = true;
					System.out.println("乌龟获胜!");
					break;
				}
				System.out.println("乌龟距终点" + (total - disTortoise) + "米");
			}
			synchronized (this) {
				if(!isOver) {
					if(disRabbit > disTortoise) {
						System.out.println("兔子领先");
					}
					else {
						System.out.println("乌龟领先");
					}
					System.out.println();
				}
			}
		}
	}	
}
public class Main {
	public static void main(String[] args) {
		Race r = new Race();
		Thread t1 = new Thread(r, "兔子");
		Thread t2 = new Thread(r, "乌龟");
		t1.start();
		t2.start();
	}
}

运行结果(不唯一):

兔子距终点90米
我跑得快,睡一觉

乌龟距终点99米
兔子领先

乌龟距终点98米
兔子领先

乌龟距终点97米
兔子领先

乌龟距终点96米
兔子领先

乌龟距终点95米
兔子领先

乌龟距终点94米
兔子领先

乌龟距终点93米
兔子领先

乌龟距终点92米
兔子领先

乌龟距终点91米
兔子领先

乌龟距终点90米
乌龟领先

乌龟距终点89米
乌龟领先

乌龟距终点88米
乌龟领先

乌龟距终点87米
乌龟领先

乌龟距终点86米
乌龟领先

乌龟距终点85米
乌龟领先

乌龟距终点84米
乌龟领先

乌龟距终点83米
乌龟领先

乌龟距终点82米
乌龟领先

乌龟距终点81米
乌龟领先

乌龟距终点80米
乌龟领先

乌龟距终点79米
乌龟领先

乌龟距终点78米
乌龟领先

乌龟距终点77米
乌龟领先

乌龟距终点76米
乌龟领先

乌龟领先

兔子距终点80米
乌龟领先

乌龟距终点75米
兔子领先

乌龟距终点74米
兔子领先

乌龟距终点73米
兔子领先

乌龟距终点72米
兔子领先

乌龟距终点71米
兔子领先

乌龟距终点70米
乌龟领先

乌龟距终点69米
乌龟领先

乌龟距终点68米
乌龟领先

乌龟距终点67米
乌龟领先

乌龟距终点66米
乌龟领先

乌龟距终点65米
乌龟领先

乌龟距终点64米
乌龟领先

乌龟距终点63米
乌龟领先

乌龟距终点62米
乌龟领先

乌龟距终点61米
乌龟领先

乌龟距终点60米
乌龟领先

乌龟距终点59米
乌龟领先

乌龟距终点58米
乌龟领先

乌龟距终点57米
乌龟领先

乌龟距终点56米
乌龟领先

乌龟距终点55米
乌龟领先

兔子距终点70米
乌龟领先

乌龟距终点54米
乌龟领先

乌龟距终点53米
乌龟领先

乌龟距终点52米
乌龟领先

兔子距终点60米
乌龟领先

乌龟距终点51米
兔子领先

乌龟距终点50米
乌龟领先

兔子距终点50米
乌龟领先

乌龟距终点49米
兔子领先

乌龟距终点48米
兔子距终点40米
我跑得快,睡一觉

兔子领先

乌龟距终点47米
兔子领先

兔子领先

乌龟距终点46米
兔子领先

乌龟距终点45米
兔子领先

兔子距终点30米
我跑得快,睡一觉

乌龟距终点44米
兔子领先

乌龟距终点43米
兔子领先

乌龟距终点42米
兔子领先

乌龟距终点41米
兔子领先

乌龟距终点40米
兔子领先

乌龟距终点39米
兔子领先

乌龟距终点38米
兔子领先

乌龟距终点37米
兔子领先

乌龟距终点36米
兔子领先

乌龟距终点35米
兔子领先

乌龟距终点34米
兔子领先

乌龟距终点33米
兔子领先

乌龟距终点32米
兔子领先

乌龟距终点31米
兔子领先

乌龟距终点30米
乌龟领先

乌龟距终点29米
乌龟领先

乌龟距终点28米
乌龟领先

乌龟距终点27米
乌龟领先

乌龟距终点26米
乌龟领先

乌龟距终点25米
乌龟领先

乌龟距终点24米
乌龟领先

乌龟距终点23米
乌龟领先

乌龟距终点22米
乌龟领先

乌龟距终点21米
乌龟领先

乌龟距终点20米
乌龟领先

乌龟距终点19米
乌龟领先

乌龟距终点18米
乌龟领先

乌龟距终点17米
乌龟领先

乌龟距终点16米
乌龟领先

乌龟距终点15米
乌龟领先

乌龟距终点14米
乌龟领先

乌龟距终点13米
乌龟领先

乌龟距终点12米
乌龟领先

乌龟距终点11米
乌龟领先

乌龟距终点10米
乌龟领先

乌龟距终点9米
乌龟领先

乌龟距终点8米
乌龟领先

乌龟距终点7米
乌龟领先

乌龟距终点6米
乌龟领先

乌龟距终点5米
乌龟领先

乌龟距终点4米
乌龟领先

乌龟距终点3米
乌龟领先

乌龟距终点2米
乌龟领先

乌龟距终点1米
乌龟领先

乌龟获胜!

二、模拟多人过独木桥

(题目编号8690)
编写多线程应用程序,模拟多人过独木桥。
独木桥每次只能通过一个人,每个人通过木桥的时间为5秒,随机生成10个人,同时准备过此独木桥,
显示一下每次通过独木桥人的姓名。需要用到随机数。
注意:(1)在开始过桥时输出:**开始过桥!过完桥后输出:**已过桥!
(2)随机选人的时候,每个人都要选到,不能重复选。

代码:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
class SingleBridge implements Runnable {
	@Override
	public void run() { 
		synchronized(this) { 
		// synchronized(this){...}保证一个进程执行该代码块内所有语句不会被其他进程打断
			System.out.println(Thread.currentThread().getName() + " 开始过桥");
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " 已过桥");
		}
	}
}
public class Main {
	static final int N = 10; // 人数
	public static void main(String[] args) throws InterruptedException {
		SingleBridge s = new SingleBridge();
		List<Thread> threads = new ArrayList<>();
		for(int i = 0; i < N; i++) {
			threads.add(new Thread(s, "name" + i));
		}
		Random random = new Random();
		while(threads.size() > 0) {
			int index = random.nextInt(threads.size()); 
			// get a random number [0,size-1]
			threads.get(index).start();
			threads.remove(index);
		}
	}
}

运行结果:

name0 开始过桥
name0 已过桥
name7 开始过桥
name7 已过桥
name9 开始过桥
name9 已过桥
name2 开始过桥
name2 已过桥
name3 开始过桥
name3 已过桥
name5 开始过桥
name5 已过桥
name6 开始过桥
name6 已过桥
name4 开始过桥
name4 已过桥
name8 开始过桥
name8 已过桥
name1 开始过桥
name1 已过桥

三、模拟多站售票

(题目编号7180)
哈尔滨火车站下面有三个火车票代售点:哈站、哈东站、哈西站,
假如哈尔滨到北京的火车票总共是200张,如何用程序来实现三个售票点同时卖票的功能。
注意:考虑线程同步问题,避免出现重复卖票问题。需要考虑同步问题。

代码:

class TicketApp implements Runnable {
	volatile int ticket = 200; // 2000效果更明显 
	volatile boolean canSell = true;
	synchronized void sell() { 
		// 这里一定要加synchronized
		// 保证一个线程在执行sell方法的过程中不会被其他线程打断
		if(ticket > 0) {
			ticket--;
			System.out.println(Thread.currentThread().getName() + " 售出一张票," 
								+ "现有余票" + ticket + "张");
		}
		else {
			canSell = false;
		}
	}
	@Override
	public void run() {
		while(canSell) {
			sell();
		}	
	}	
}
public class Main {
	public static void main(String[] args) {
		TicketApp app = new TicketApp();
		Thread t1 = new Thread(app, "哈站");
		Thread t2 = new Thread(app, "哈东站");
		Thread t3 = new Thread(app, "哈西站");
		t1.start();
		t2.start();
		t3.start();	
	}
}

运行结果:

哈东站 售出一张票,现有余票199张
哈东站 售出一张票,现有余票198张
哈东站 售出一张票,现有余票197张
哈东站 售出一张票,现有余票196张
哈东站 售出一张票,现有余票195张
哈东站 售出一张票,现有余票194张
哈东站 售出一张票,现有余票193张
哈东站 售出一张票,现有余票192张
哈东站 售出一张票,现有余票191张
哈东站 售出一张票,现有余票190张
哈东站 售出一张票,现有余票189张
哈东站 售出一张票,现有余票188张
哈东站 售出一张票,现有余票187张
哈东站 售出一张票,现有余票186张
哈东站 售出一张票,现有余票185张
哈东站 售出一张票,现有余票184张
哈东站 售出一张票,现有余票183张
哈东站 售出一张票,现有余票182张
哈东站 售出一张票,现有余票181张
哈东站 售出一张票,现有余票180张
哈东站 售出一张票,现有余票179张
哈东站 售出一张票,现有余票178张
哈东站 售出一张票,现有余票177张
哈东站 售出一张票,现有余票176张
哈东站 售出一张票,现有余票175张
哈东站 售出一张票,现有余票174张
哈东站 售出一张票,现有余票173张
哈东站 售出一张票,现有余票172张
哈东站 售出一张票,现有余票171张
哈东站 售出一张票,现有余票170张
哈东站 售出一张票,现有余票169张
哈东站 售出一张票,现有余票168张
哈西站 售出一张票,现有余票167张
哈西站 售出一张票,现有余票166张
哈西站 售出一张票,现有余票165张
哈西站 售出一张票,现有余票164张
哈西站 售出一张票,现有余票163张
哈西站 售出一张票,现有余票162张
哈西站 售出一张票,现有余票161张
哈西站 售出一张票,现有余票160张
哈西站 售出一张票,现有余票159张
哈西站 售出一张票,现有余票158张
哈西站 售出一张票,现有余票157张
哈西站 售出一张票,现有余票156张
哈西站 售出一张票,现有余票155张
哈西站 售出一张票,现有余票154张
哈西站 售出一张票,现有余票153张
哈西站 售出一张票,现有余票152张
哈西站 售出一张票,现有余票151张
哈西站 售出一张票,现有余票150张
哈西站 售出一张票,现有余票149张
哈西站 售出一张票,现有余票148张
哈西站 售出一张票,现有余票147张
哈西站 售出一张票,现有余票146张
哈西站 售出一张票,现有余票145张
哈西站 售出一张票,现有余票144张
哈西站 售出一张票,现有余票143张
哈西站 售出一张票,现有余票142张
哈西站 售出一张票,现有余票141张
哈西站 售出一张票,现有余票140张
哈西站 售出一张票,现有余票139张
哈西站 售出一张票,现有余票138张
哈西站 售出一张票,现有余票137张
哈站 售出一张票,现有余票136张
哈站 售出一张票,现有余票135张
哈站 售出一张票,现有余票134张
哈站 售出一张票,现有余票133张
哈站 售出一张票,现有余票132张
哈站 售出一张票,现有余票131张
哈站 售出一张票,现有余票130张
哈站 售出一张票,现有余票129张
哈站 售出一张票,现有余票128张
哈站 售出一张票,现有余票127张
哈站 售出一张票,现有余票126张
哈站 售出一张票,现有余票125张
哈站 售出一张票,现有余票124张
哈站 售出一张票,现有余票123张
哈站 售出一张票,现有余票122张
哈站 售出一张票,现有余票121张
哈站 售出一张票,现有余票120张
哈站 售出一张票,现有余票119张
哈站 售出一张票,现有余票118张
哈站 售出一张票,现有余票117张
哈站 售出一张票,现有余票116张
哈站 售出一张票,现有余票115张
哈站 售出一张票,现有余票114张
哈站 售出一张票,现有余票113张
哈站 售出一张票,现有余票112张
哈站 售出一张票,现有余票111张
哈站 售出一张票,现有余票110张
哈站 售出一张票,现有余票109张
哈站 售出一张票,现有余票108张
哈站 售出一张票,现有余票107张
哈站 售出一张票,现有余票106张
哈站 售出一张票,现有余票105张
哈站 售出一张票,现有余票104张
哈站 售出一张票,现有余票103张
哈站 售出一张票,现有余票102张
哈站 售出一张票,现有余票101张
哈站 售出一张票,现有余票100张
哈站 售出一张票,现有余票99张
哈站 售出一张票,现有余票98张
哈站 售出一张票,现有余票97张
哈站 售出一张票,现有余票96张
哈站 售出一张票,现有余票95张
哈站 售出一张票,现有余票94张
哈站 售出一张票,现有余票93张
哈站 售出一张票,现有余票92张
哈站 售出一张票,现有余票91张
哈站 售出一张票,现有余票90张
哈站 售出一张票,现有余票89张
哈站 售出一张票,现有余票88张
哈站 售出一张票,现有余票87张
哈站 售出一张票,现有余票86张
哈站 售出一张票,现有余票85张
哈站 售出一张票,现有余票84张
哈站 售出一张票,现有余票83张
哈站 售出一张票,现有余票82张
哈站 售出一张票,现有余票81张
哈站 售出一张票,现有余票80张
哈站 售出一张票,现有余票79张
哈站 售出一张票,现有余票78张
哈站 售出一张票,现有余票77张
哈站 售出一张票,现有余票76张
哈站 售出一张票,现有余票75张
哈站 售出一张票,现有余票74张
哈站 售出一张票,现有余票73张
哈站 售出一张票,现有余票72张
哈站 售出一张票,现有余票71张
哈站 售出一张票,现有余票70张
哈站 售出一张票,现有余票69张
哈站 售出一张票,现有余票68张
哈站 售出一张票,现有余票67张
哈站 售出一张票,现有余票66张
哈站 售出一张票,现有余票65张
哈站 售出一张票,现有余票64张
哈站 售出一张票,现有余票63张
哈站 售出一张票,现有余票62张
哈站 售出一张票,现有余票61张
哈站 售出一张票,现有余票60张
哈站 售出一张票,现有余票59张
哈站 售出一张票,现有余票58张
哈站 售出一张票,现有余票57张
哈站 售出一张票,现有余票56张
哈站 售出一张票,现有余票55张
哈站 售出一张票,现有余票54张
哈站 售出一张票,现有余票53张
哈站 售出一张票,现有余票52张
哈站 售出一张票,现有余票51张
哈站 售出一张票,现有余票50张
哈站 售出一张票,现有余票49张
哈站 售出一张票,现有余票48张
哈站 售出一张票,现有余票47张
哈站 售出一张票,现有余票46张
哈站 售出一张票,现有余票45张
哈站 售出一张票,现有余票44张
哈站 售出一张票,现有余票43张
哈站 售出一张票,现有余票42张
哈站 售出一张票,现有余票41张
哈站 售出一张票,现有余票40张
哈站 售出一张票,现有余票39张
哈站 售出一张票,现有余票38张
哈站 售出一张票,现有余票37张
哈站 售出一张票,现有余票36张
哈站 售出一张票,现有余票35张
哈站 售出一张票,现有余票34张
哈站 售出一张票,现有余票33张
哈站 售出一张票,现有余票32张
哈站 售出一张票,现有余票31张
哈站 售出一张票,现有余票30张
哈站 售出一张票,现有余票29张
哈站 售出一张票,现有余票28张
哈站 售出一张票,现有余票27张
哈站 售出一张票,现有余票26张
哈站 售出一张票,现有余票25张
哈站 售出一张票,现有余票24张
哈站 售出一张票,现有余票23张
哈站 售出一张票,现有余票22张
哈站 售出一张票,现有余票21张
哈站 售出一张票,现有余票20张
哈站 售出一张票,现有余票19张
哈站 售出一张票,现有余票18张
哈站 售出一张票,现有余票17张
哈站 售出一张票,现有余票16张
哈站 售出一张票,现有余票15张
哈站 售出一张票,现有余票14张
哈站 售出一张票,现有余票13张
哈站 售出一张票,现有余票12张
哈站 售出一张票,现有余票11张
哈站 售出一张票,现有余票10张
哈站 售出一张票,现有余票9张
哈站 售出一张票,现有余票8张
哈站 售出一张票,现有余票7张
哈站 售出一张票,现有余票6张
哈站 售出一张票,现有余票5张
哈西站 售出一张票,现有余票4张
哈西站 售出一张票,现有余票3张
哈西站 售出一张票,现有余票2张
哈西站 售出一张票,现有余票1张
哈西站 售出一张票,现有余票0张

附录:随机数

Java中常见的有三种随机方式:

  1. java.lang.Math.random():返回一个0≤x<1.0的double值。在一次JVM的运行中,只创建一个伪随机数生成器,之后对该方法的所有调用,都是通过该生成器来生成随机数。此方法是同步的,可以在多线程中正确运行,但如果有大量线程需要高速生成随机数,对该生成器的争用会降低效率。
  2. java.util.Random类:实现更多功能的随机数生成。每创建一个对象就会创建一个伪随机数生成器,只要seed不同,生成的随机数序列就会不同;seed相同时,生成的随机数序列完全相同。该类有两种构造方法:
    Random():用某个seed创建一个随机数生成器。在一次JVM的运行中,创建每个随机数生成器的seed都不同。也就是说,用此方法创建的多个random对象,生成的随机数序列一定不同(除非用setSeed方法改变seed)。在多线程中,可以每个线程中创建一个random对象,这样就避免了对随机数生成器的争用。
    Random(long seed):用给定seed创建随机数生成器。可通过相同seed创建random对象,生成相同的随机数序列。
  3. java.security.SecureRandom类:提供强加密随机数生成器。

如果不需要用到多线程,也不需要多样化的随机数生成方法,选择Math.random()即可。
注意,用于生成int随机数时,(int) (x*Math.random())生成的随机数范围为0~x-1。

Java并发编程精讲

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/106453019