理解多线程的概念,掌握创建、管理和控制 Java 线程对象的方法,包括创建 Java线程对象、改变线程状态、设置线程优先级及控制线程调度等方法,掌握实现线程互斥和线程同步的方法。
1、编写一个有两个线程的程序,第一个线程用来计算1~100之间的奇数及个数,第二个线程用来计算1-100之间的偶数及个数。
2、编写一个Java应用程序,在主线程中再创建两个线程,要求线程经历四种状态:新建,运行、中断和死亡。按模板要求,将【代码1】~【代码8】替换为Java程序代码。
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 args[]) {
Rabit rabit;
rabit =【代码5】 // 新建线程rabit
Tortoise tortoise =【代码6】 // 新建线程tortoise
【代码7】 // 启动线程tortoise
【代码8】 // 启动线程rabit
}
}
*3、编写Java应用程序模拟5个人排队买票。售票员只有1张五元的钱,电影票五元钱一张。假设5个人的名字及排队顺序是:赵、钱、孙、李、周。 “赵”拿1张二十元的人民币买2张票,“钱”拿1张二十元的人民币买1张票,“孙”1张十元的人民币买1张票,“李”拿1张十元的人民币买2张票,“周”拿1张五元的人民币买1张票。
要求售票员按如下规则找赎:
(1)二十元买2张票,找零:1张十元;不许找零2张五元。
(2)二十元买1张票,找零:1张十元,1张五元;不许找零3张五元。 (3)十元买一张票,找零1张五元。
1、
import java.lang.Runnable;
class Even extends Thread{//偶数线程
private int first;
Even(String name,int fisrt){
super(name);
this.first=first;
}
int count1=0;
public void run(){
System.out.print("\n"+this.getName()+": ");
for(int k=first;k<101;k+=2){
System.out.print(k+" ");
count1+=1;
}
System.out.println(this.getName()+"结束!");
System.out.println(this.getName()+"数据个数为:"+count1);
}
public static void main(String args[]){
System.out.println("currentThread="+Thread.currentThread().getName());
Even even=new Even("偶数线程",2);
even.start();
System.out.println("activeCount="+Thread.activeCount());//输出当前活动线程数
}
}
class Odd extends Thread{
int first;
Odd(String name,int first){
super(name);
this.first=first;
}
int count2=0;
public void run(){
System.out.print("\n"+this.getName()+": ");
for(int j=first;j<101;j+=2){
System.out.print(j+" ");
count2+=1;
}
System.out.println(this.getName()+"结束!");
System.out.println(this.getName()+"数据个数为:"+count2);
}
public static void main(String args[]){
System.out.println("currentThread="+Thread.currentThread().getName());
Odd odd=new Odd("奇数线程",1);
odd.start();
System.out.println("activeCount="+Thread.activeCount());
}
}
2、
class Tortoise extends Thread{
int sleepTime=0,liveLength=0;
public Tortoise(String name,int sleepTime, int liveLength){
super(name); //设置线程的名字name
this.sleepTime=sleepTime;
this.liveLength=liveLength;
}
public void run()
{
while(true){
liveLength--;
System.out.println("@_@");
try{
sleep(sleepTime); // 让线程调用sleep()方法进入中断状态
}catch (InterruptedException e) {}
if (liveLength<=0){
System.out.println(getName()+"进入死亡状态\n");
break;// 结束run()方法的语句
}
}
}
}
class Rabit extends Thread{
int sleepTime=0, liveLength=0;
public Rabit(String name,int sleepTime, int liveLength) {
super(name); // 调用父类构造函数,设置线程的名字为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;
}
}
}
}
class ThreadExample {
public static void main(String args[]) {
Rabit rabit;
rabit =new Rabit("兔子",10,10); // 新建线程rabit
Tortoise tortoise =new Tortoise("乌龟",10,10);// 新建线程tortoise
tortoise.start(); // 启动线程tortoise
rabit.start();// 启动线程rabit
}
}
3、
import java.util.Objects;
class A{
int five=1,ten=0,twenty=0;
public synchronized void AA(int money,int amount){
if(money==20&&amount==2){
System.out.println("赵来买票");
twenty++;
if(ten<1){
try{
System.out.println("请赵先等待,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
this.wait(500);
}
catch(Exception e){
}
}
ten--;
notifyAll();
System.out.println("赵已买票,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
}
else if(money==20&&amount==1){
System.out.println("钱来买票");
twenty++;
if(ten<1||five<1){
try{
System.out.println("请钱先等待,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
this.wait(500);
}
catch(Exception e){
}
}
ten--;
five--;
notifyAll();
System.out.println("钱已买票,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
}
else if(money==10&&amount==1){
System.out.println("孙来买票");
ten++;
if(five<1){
try{
System.out.println("请孙先等待,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+",个二十元");
this.wait(500);
}
catch(Exception e){
}
}
five--;
notifyAll();
System.out.println("孙已买票,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
}
else if(money==10&&amount==2){
System.out.println("李来买票");
ten++;
notifyAll();
System.out.println("李已买票,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
}
else if(money==5&&amount==1){
System.out.println("周来买票");
five++;
notifyAll();
System.out.println("周已买票,售货员此时有: "+five+"个五元, "+ten+"个十元, "+twenty+"个二十元");
}
}
}
class Tickets extends Thread{
private int money,amount;//钱数,票数
static final A a=new A();
Tickets(String name,int money,int amount){
super(name);
this.money=money;
this.amount=amount;
}
public synchronized void run(){
if(money==20&&amount==2)
a.AA(20,2);
else if(money==20&&amount==1)
a.AA(20,1);
else if(money==10&&amount==1)
a.AA(10,1);
else if(money==10&&amount==2)
a.AA(10,2);
else if(money==5&&amount==1)
a.AA(5,1);
}
public static void main(String args[]){
Tickets a1=new Tickets("赵",20,2);
Tickets a2=new Tickets("钱",20,1);
Tickets a3=new Tickets("孙",10,1);
Tickets a4=new Tickets("李",10,2);
Tickets a5=new Tickets("周",5,1);
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
}
1、关于第一个实验:
在前面的方法中我使用的是继承Thread类,并且分开两个类来分别输出偶数线程和奇数线程,虽然有些麻烦,但是如果都写在一个类中,由于线程执行顺序的不确定性就会出现偶数线程和奇数线程交替运行,序列交替输出的情况,并且不便于我观察实验数据。
以下是我关于这种情况做的实验:
a.继承Thread类
代码:
import java.lang.Runnable;
public class Number1 extends Thread{
private int first;
public Number1(String name,int first){
super(name);
this.first=first;
}
int count=0;
public void run(){
System.out.print("\n"+this.getName()+": ");
for(int i=first;i<101;i+=2){
System.out.print(i+" ");
count+=1;
}
System.out.println(this.getName()+"结束!");
System.out.println(this.getName()+"数据个数为:"+count);
}
public static void main(String args[]){
System.out.println("currentThread="+Thread.currentThread().getName());
Number1 odd=new Number1("奇数线程",1);
Number1 even=new Number1("偶数线程",2);
odd.start();
even.start();
System.out.println("activeCount="+Thread.activeCount());//输出当前活动线程数
}
}
b.实现Runnable接口
代码:
import java.lang.Runnable;
public class Number2 implements Runnable{
private int first;
public Number2(int first){
this.first=first;
}
int count=0;
public void run(){
for(int i=first;i<101;i+=2){
System.out.print(i+" ");
count+=1;
}
System.out.println("结束!");
}
public static void main(String args[]){
Number2 target=new Number2(1);//target是接口对象,不是线程对象,但是只有线程对象才能启动线程(调用start()方法),因此再创建一个线程对象(odd)。
Thread odd=new Thread(target,"奇数线程");
odd.start();
new Thread(new Number2(2),"偶数线程").start();//另一种形式
}
}
显然,当序列长度比较长时,就会出现多个序列交替输出的情况,不便于我们观察线程的执行情况。
(2)
课本上的类似实验:NumberThread类,分别输出奇数线程和偶数线程:
调整程序,观察不同的运行成果:
import java.lang.Runnable;
public class NumberThread extends Thread{
private int first;
public NumberThread(String name,int first){
super(name);
this.first=first;
}
public void run(){
System.out.print("\n"+this.getName()+": ");
for(int i=first;i<500;i+=2){//三次情况分别对应50、5、500
System.out.print(i+" ");
}
System.out.println(this.getName()+"结束!");
}
public static void main(String args[]){
System.out.println("currentThread="+Thread.currentThread().getName());
NumberThread thread_odd=new NumberThread("奇数线程",1);
NumberThread thread_even=new NumberThread("偶数线程",2);
thread_odd.start();
thread_even.start();
System.out.println("activeCount="+Thread.activeCount());//输出当前活动线程数
}
}
思考题7-1中的2: 在main()方法中输出较长数据序列后,再启动奇数线程和偶数线程,如下图所示:
结果说明main()方法中输出的内容对于线程的执行并没有影响,线程执行的结果不变。
设置优先级之后奇数线程要比偶数线程更快进入运行状态,实验多次几乎每一次都是奇数线程先运行,但是虽然奇数线程更快运行,但是偶数线程不久之后也会运行,两个线程并不会完全分离,只是开始时间的先后而已,最后序列还是会交替输出。
2、第二个实验:
3、第三个实验:
(1)实验思路:
实验应该从售票员的角度出发,因为是售票员手里的钱来决定能否执行用户买票的线程。
售票员:手里有5元——》顾客买票——》先收钱——》钱不够找——》顾客等待——》循环。。。——》钱够找——》找钱——》结束
(2)问题:
1.java.lang.IllegalMonitorStateException: 这个问题是因为调用的wait()方法和notify()方法,没有在synchronized block中,即应该在由synchronized修饰的方法中调用Object类的wait()方法和notify()方法。
(3)原先我直接把五个线程直接写入了run()方法中,并且定义了局部变量:int
five=1,ten=0,twenty=0;//分别表示起始时售货员手中五元、十元和二十元的个数。
但是每一次线程之间单独进行这三个变量的运算,就导致最后出现-1这样明显错误的结果。
正确做法是再重新声明一个类A,在A中声明成员变量
five、ten、twenty。然后在A中实现线程的过程,再在线程的设计中声明A类的对象,在run()中调用A中的方法,最后得到正确结果。