本答案为本人个人编辑,仅供参考,如果读者发现错误,请私信本人或在下方评论,一起进步!
题目链接:https://www.sxt.cn/Java_jQuery_in_action/eleven-task.html
一、 选择题
1.以下选项中可以填写到横线处,让代码正确编译和运行的是( A )。(选择一项)
public class Test implements Runnable {
public static void main(String[] args) {
___________________________________
t.start();
System.out.println("main");
}
public void run() {
System.out.println("thread1!");
}
}
A.Thread t = new Thread(new Test());
B.Test t = new Test();
C.Thread t = new Test();
D.Thread t = new Thread();
2.当线程调用start( )后,其所处状态为( C )。(选择一项)
A.阻塞状态
B.运行状态
C.就绪状态
D.新建状态
3.以下选项中关于Java中线程控制方法的说法正确的是( AD )。(选择二项)
A.t.join ( ) 的作用是阻塞指定线程等到另一个线程完成以后再继续执行
B.sleep ( ) 的作用是让当前正在执行线程暂停,线程将转入就绪状态
C.yield ( ) 的作用是使线程停止运行一段时间,将处于阻塞状态
D.setDaemon( )的作用是将指定的线程设置成后台线程
4.Java中线程安全问题是通过关键字( C )解决的。(选择一项)
A.finally
B.wait( )
C.synchronized
D.notify( )
5.以下说法中关于线程通信的说法错误的是( D )。(选择一项)
A.可以调用wait()、notify()、notifyAll()三个方法实现线程通信
B.wait()、notify()、notifyAll()必须在synchronized方法或者代码块中使用
C.wait()有多个重载的方法,可以指定等待的时间
D.wait()、notify()、notifyAll()是Object类提供的方法,子类可以重写
二、 简答题
1. 简述程序、进程和线程的联系和区别。
程序:静态概念。Java中程序包括源程序和字节码文件。
进程:执行起来的程序,动态概念。每个进程包括CPU、Data、Code
线程:是进程的一部分,是进程中一个单一的连续控制流程。
2. 创建线程的两种方式分别是什么?各有什么优缺点。
1⃣️继承Thread类
2⃣️实现Runnable接口
3⃣️实现Callable接口
对比
| 优点 | 缺点 |
继承Thread类 | 创建 Thread的实例来创建新的线程 | 存在单一继承问题 |
实现Runnable接口 | 解决单一继承问题 | 需要创建Thread对象 |
实现Callable接口 | 比Runnable强大,有返回值,可Throws Exception | 使用麻烦 |
3. sleep、yield、join方法的区别?
sleep:线程暂停,进入阻塞状态,抱着资源不放,直到休眠时间满了,进入就绪状态
join: 线程暂停,进入阻塞状态,等待其他线程执行完毕再执行
yield: 线程挂起,进入就绪状态,让出CPU资源
wait: 线程等待,进入阻塞状态,资源被其他线程使用,直到notify()、notifyAll()被调用进入同步阻塞,锁可用进入就绪状态
4. synchronize修饰的语句块,如下面的代码。是表示该代码块运行时必须获得account对象的锁。如果没有获得,会有什么情况发生?
synchronized (account) {
if(account.money-drawingNum<0){
return;
}
}
答:使用多线程时,出现多次提现问题,提现总金额大于账户总余额。
5. Java中实现线程通信的三个方法及其作用。
线程通信的方法:
▪ wait():线程等待,进入阻塞状态,资源被其他线程使用,直到notify()、notifyAll()被调用进入同步阻塞,锁可用进入就绪状态
▪ notify():唤醒阻塞状态的一个线程,使之进入就绪状态
▪ notifyAll():唤醒阻塞状态的所有线程,使之进入就绪状态
三、 编码题
1. 设计一个多线程的程序如下:设计一个火车售票模拟程序。假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况。
package com.name.thread;
public class Web12306 {
public static void main(String[] args) {
Tickets ticket = new Tickets();
new Thread(ticket,"黄牛").start();
new Thread(ticket,"灰牛").start();
new Thread(ticket,"黑牛").start();
}
}
class Tickets implements Runnable{
private int tickets = 1;
@Override
public void run() {
while(true){
if(tickets > 100) {
break;
}
System.out.println(Thread.currentThread().getName()+"-->抢到了第"+tickets+++"张票");
}
}
}
2. 编写两个线程,一个线程打印1-52的整数,另一个线程打印字母A-Z。打印顺序为12A34B56C….5152Z。即按照整数和字母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数52和字母Z结束。
要求:
1) 编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。
2) 在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,否则就输出i,使用notifyAll()进行唤醒其它线程。
3) 在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待,否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。
4) 编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。
5) 编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。
6) 编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程。
package com.name.thread;
/*
1) 编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。
2) 在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,否则就输出i,使用notifyAll()进行唤醒其它线程。
3) 在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待,否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。
4) 编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。
5) 编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。
6) 编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程*/
public class Test {
public static void main(String[] args) {
Printer printer = new Printer();
NumberPrinter numP = new NumberPrinter(printer);
LetterPrinter letP = new LetterPrinter(printer);
numP.start();
letP.start();
}
}
class Printer{
private int index = 1;
public boolean print(int i) {
synchronized(this) {
if((index%3)!=0) {
System.out.print(i);
index++;
return true;
}else{
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return false;
}
public boolean print(char c) {
synchronized(this) {
if((index%3)==0) {
System.out.print(c);
index++;
return true;
}else{
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return false;
}
}
class NumberPrinter extends Thread{
private Printer p;
public NumberPrinter(Printer p) {
this.p = p;
}
@Override
public void run() {
int i = 1;
while(i<53) {
if(p.print(i) == true) {
i++;
}if(i == 53) {
p.print(i);
}
}
}
}
class LetterPrinter extends Thread{
private Printer p;
public LetterPrinter(Printer p) {
this.p = p;
}
@Override
public void run() {
int i = 65;
while(i<91) {
char temp = (char) (i);
if(p.print(temp) == true) {
i++;
}
}
}
}
3. 编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。
package com.name.thread;
/*
* 3. 编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,
* 有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。
*/
public class CrossingCave {
public static void main(String[] args) {
Cave cave = new Cave();
People A = new People("A",cave);
People B = new People("B",cave);
People C = new People("C",cave);
People D = new People("D",cave);
People E = new People("E",cave);
People F = new People("F",cave);
People G = new People("G",cave);
People H = new People("H",cave);
People I = new People("I",cave);
People J = new People("J",cave);
A.start();
B.start();
C.start();
D.start();
E.start();
F.start();
G.start();
H.start();
I.start();
J.start();
}
}
/**
* 山洞
* @ClassName: Cave
* @Date 2019年10月12日 下午8:45:36
*/
class Cave{
private int index = 1;
public synchronized void cross(String name) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"第"+index+"个通过");
index++;
}
}
/**
* 人
* @ClassName: People
* @Date 2019年10月12日 下午8:45:53
*/
class People extends Thread{
private String name;
Cave cave;
public People(String name, Cave cave) {
super();
this.name = name;
this.cave = cave;
}
@Override
public void run() {
cave.cross(name);
}
}