[1]实验目的:理解多线程的概念,掌握创建、管理和控制Java线程对象的方法,包括创建Java线程对象、改变线程状态、设置线程优先级及控制线程调度等方法,掌握实现线程互斥和线程同步的方法。
[2]实验内容:
1、编写一个有两个线程的程序,第一个线程用来计算1~100之间的偶数及个数,第二个线程用来计算1-100之间的偶数及个数。
package experiment4;
class NumberRunnable implements Runnable{
private final int first;
NumberRunnable(int first){
this.first=first;
}
@Override
public void run() {
for (int i=first;i<=100;i+=2){
System.out.println(i+ " ");
}
System.out.println("结束!");
}
}
public class Exp4_1 {
public static void main(String[] args){
NumberRunnable runnable1 = new NumberRunnable(1);
NumberRunnable runnable2 = new NumberRunnable(2);
Thread thread_odd = new Thread(runnable1,"奇数线程");
Thread thread_even = new Thread(runnable2,"偶数线程");
thread_odd.start();
thread_even.start();
}
}
2、编写一个Java应用程序,在主线程中再创建两个线程,要求线程经历四种状态:新建,运行、中断和死亡。按模板要求,将【代码1】~【代码8】替换为Java程序代码。
class Tortoise extends Thread
{ int sleepTime=0, liveLength=0;
public Tortoise(String name,int sleepTime, int liveLength)
{
this.sleepTime=sleepTime;
this.liveLength=liveLength;
【代码1】 // 设置线程的名字为name
}
public void run()
{ while (true )
{
liveLength--;
System.out.println("@_@");
try{
【代码2】 // 让线程调用sleep()方法进入中断状态
}
catch (InterruptedException e) {}
if (liveLength<=0 )
{
System.out.println(getName()+"进入死亡状态\n");
【代码3】 // 结束run()方法的语句
}
}
}
}
class Rabit extends Thread
{
int sleepTime=0, liveLength=0;
public Rabit(String name,int sleepTime, int liveLength)
{
【代码4】 // 调用父类构造函数,设置线程的名字为name
this.sleepTime=sleepTime;
this.liveLength=liveLength;
}
public void run()
{
while (true )
{
liveLength--;
System.out.println("*_*");
try{
sleep( sleepTime);
}
catch (InterruptedException e) {}
if (liveLength<=0 )
{
System.out.println(getName()+"进入死亡状态\n");
break;
}
}
}
}
public class ThreadExample
{
public static void main(String a[])
{
Rabit rabit;
rabit =【代码5】 // 新建线程rabit
Tortoise tortoise =【代码6】 // 新建线程tortoise
【代码7】 // 启动线程tortoise
【代码8】 // 启动线程rabit
}
}
package experiment4;
class Tortoise extends Thread
{
int sleepTime,liveLength;
public Tortoise(String name,int sleepTime,int liveLength)
{
super(name);//【代码1】设置线程的名字为name
this.sleepTime=sleepTime;
this.liveLength=liveLength;
}
public void run()
{
while(true)
{
liveLength--;
System.out.println("@_@");
try {
sleep(sleepTime);//【代码2】让线程调用sleep()方法进入中断状态
}
catch(InterruptedException ignored) {}
if(liveLength<=0)
{
System.out.println(getName()+"进入死亡状态\n");
break;//【代码3】结束run()方法的语句
}
}
}
}
class Rabbit extends Thread
{
int sleepTime,liveLength;
public Rabbit(String name,int sleepTime,int liveLength)
{
super(name);//【代码4】调用父类构造函数,设置线程的名字为name
this.sleepTime=sleepTime;
this.liveLength=liveLength;
}
public void run()
{
while(true)
{
liveLength--;
System.out.println("*_*");
try {
sleep(sleepTime);
}
catch(InterruptedException ignored) {}
if(liveLength<=0)
{
System.out.println(getName()+"进入死亡状态\n");
break;
}
}
}
}
public class Exp4_2 {
public static void main(String[] args){
Rabbit rabbit;
rabbit = new Rabbit("Rabbit",200,100);
Tortoise tortoise = new Tortoise("Tortoise", 200,100);
rabbit.start();
tortoise.start();
}
}
*3、编写Java应用程序模拟5个人排队买票。售票员只有1张五元的钱,电影票五元钱一张。假设5个人的名字及排队顺序是:赵、钱、孙、李、周。“赵”拿1张二十元的人民币买2张票,“钱”拿1张二十元的人民币买1张票,“孙”1张十元的人民币买1张票,“李”拿1张十元的人民币买2张票,“周”拿1张五元的人民币买1张票。
要求售票员按如下规则找赎:
(1)二十元买1张票,找零:1张十元;不许找零2张五元。
(2)二十元买1张票,找零:1张十元,1张五元;不许找零3张五元。
(3)十元买一张票,找零1张五元。
package experiment4;
import java.util.Objects;
class BuyTickets //临界资源
{
private static int five =1, ten =0, twenty =0;//钱的张数可以根据这个来统计
public synchronized void buy(int money,int count) {
//20元买两张票
if (money == 20 && count == 2) {
System.out.println("赵来买票");
twenty++;//先收下钱
//开始不能找钱
if(ten <1)
{
try {
System.out.println("赵稍等"+" 此时售票员手中的票为:5元有 "+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty);
this.wait();
} catch (InterruptedException e) {}
}
//之后的没有办法成功找钱,就继续进入等待状态
while(ten <1) {
try {
this.wait();
} catch (InterruptedException e) {}
}
ten--;//把钱找给他
notify();//唤醒
System.out.println("找给赵1张10元,赵结束"+" 此时售票员手中的票为:5元有"+ five +",10元有:"+ ten +",20元有:"+ twenty);
}
//20元买1张票
else if (money == 20 && count == 1) {
twenty++;
System.out.println("钱来买票");
//找零:1张十元,1张五元
if(ten <1|| five <1)
{try {
System.out.println("钱稍等"+" 此时售票员手中的票为:5元有 "+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty);
this.wait();
} catch (InterruptedException e) {}
}
while(ten <1|| five <1) {
try {
this.wait();
} catch (InterruptedException e) {}
}
ten--;
five--;
notify();
System.out.println("找给钱1张10元、1张5元,钱结束"+" 此时售票员手中的票为:5元有"+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty
);
}
//10元买1张票
else if (money == 10 && count == 1) {
ten++;
System.out.println("孙来买票");
if(five <1)
{
try {
System.out.println("孙稍等"+" 此时售票员手中的票为:5元有 "+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty);
this.wait();
} catch (InterruptedException e) {}
}
while(five <1) {
try {
this.wait();
} catch (InterruptedException e) {}
}//没有办法成功找钱,就继续进入等待状态
five--;
notify();//唤醒
System.out.println("找给孙1张5元,孙结束"+" 此时售票员手中的票为:5元有"+ five +",10元有:"+ ten +",20元有:"+ twenty);
}
//10元买2张票
else if (money == 10 && count == 2) {
ten++;
System.out.println("李来买票");
System.out.println("李结束"+" 此时售票员手中的票为:5元有 "+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty);
notify();
}
//5元买1张票
else if (money == 5 && count == 1) {
five++;
System.out.println("周来买票");
System.out.println("周结束"+" 此时售票员手中的票为:5元有 "+ five +" ,10元有:"+ ten +" ,20元有:"+ twenty);
notify();
}
}
}
class customer extends Thread//继承Thread类,创建多个线程
{
String name;
static final BuyTickets buyTickets = new BuyTickets();
public customer(String name) {
this.name = name;
}
public void run() {
if (Objects.equals(name, "赵")) buyTickets.buy(20, 2);
else if (Objects.equals(name, "钱")) buyTickets.buy(20, 1);
else if (Objects.equals(name, "孙")) buyTickets.buy(10, 1);
else if (Objects.equals(name, "李")) buyTickets.buy(10, 2);
else if (Objects.equals(name, "周")) buyTickets.buy(5, 1);
}
}
public class Exp4_3 {
public static void main(String[] args) {
new customer("赵").start();
new customer("钱").start();
new customer("孙").start();
new customer("李").start();
new customer("周").start();
}
}
[3]实验分析
通过此次实验,熟悉了在Java中进行多线程编程的能力。在第三题中,由于买票的情况和找钱的情况有限,所有我将各种情况均枚举出来,作为临界区。临界资源则是售票员手里的各种钱的数量,同时这也作为各线程之间进行通信的信号量。
将五个人买票当作五个线程来进行,在临界区的方法声明中加入synchronized的关键字,实现线程互斥。
实验结果每次运行均不一样,但是都能符合实际。