一、 选择题
1.以下选项中可以填写到横线处,让代码正确编译和运行的是(AD)。
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();
A:显示main thread1
D:显示main
2.当线程调用start( )后,其所处状态为( C)。
A.阻塞状态
B.运行状态
C.就绪状态
D.新建状态
3.以下选项中关于Java中线程控制方法的说法正确的是(AD)。
A.t.join ( ) 的作用是阻塞指定线程等到另一个线程完成以后再继续执行
B.sleep ( ) 的作用是让当前正在执行线程暂停,线程将转入就绪状态
C.yield ( ) 的作用是使线程停止运行一段时间,将处于阻塞状态
D.setDaemon( )的作用是将指定的线程设置成后台线程
B:执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
C: yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。
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类提供的方法,子类可以重写
wait()、notify()、notifyAll()均由final修饰,子类不能重写
二、 简答题
1. 简述程序、进程和线程的联系和区别。
程序是一组指令的集合,它是静态的实体,没有执行的含义。而进程是一个动态的实体,有自己的生命周期。一般说来,一个进程肯定与一个程序相对应,并且只有一个,但是一个程序可以有多个进程,或者一个进程都没有。除此之外,进程还有并发性和交往性。简单地说,进程是程序的一部分,程序运行的时候会产生进程。
区别 | 进程 | 线程 |
---|---|---|
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销 | 线程可以看作是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小 |
所处环境 | 在操作系统中能够同时运行多个任务(程序) | 在同一应用程序中有多个顺序流同时执行 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 系统在运行的时候不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。那就是说,除了CPU之外(线程在运行的时候要占用CPU资源),计算机内部的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。 |
包含关系 | 没有线程的进程是可以被看作单线程的,如果一个进程拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成 | 线程是进程的一部分,所以线程有的时候被称作为轻权线程或者轻量级线程。 |
2. 创建线程的两种方式分别是什么?各有什么优缺点。
共有三种方法:
- 继承Thread类
- 实现Runnable接口(多用接口)(方便共享资源)
- 实现Callable接口(不重要)与 Runnable 相比,Callable 可以有返回值
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。
- Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
- 类可能只要求可执行就行,继承整个 Thread 类开销过大。
3. sleep、yield、join方法的区别?
- sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。
- yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞,你写在哪个线程体中,哪个线程体就阻塞了。
4. synchronize修饰的语句块,如下面的代码。是表示该代码块运行时必须获得account对象的锁。如果没有获得,会有什么情况发生?
synchronized (account) {
if(account.money-drawingNum<0){
return;
}
}
会导致线程不安全,数据不准确
5. Java中实现线程通信的三个方法及其作用。
- wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。
- notify():唤醒在此同步监视器上等待的单个线程。
- notifyAll():唤醒在此同步监视器上等待的所有线程。
三、 编码题
- 设计一个多线程的程序如下:设计一个火车售票模拟程序。假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况。
package week11_homework;
/**
* 火车售票模拟程序
*
* @author Jeloys
*
*/
public class TrainTicket {
public static void main(String[] args) {
TrainStation station = new TrainStation();
new Thread(station,"售票点1").start();
new Thread(station,"售票点2").start();
new Thread(station,"售票点3").start();
new Thread(station,"售票点4").start();
new Thread(station,"售票点5").start();
}
}
//火车站
class TrainStation implements Runnable {
private int ticketNums = 100;
private boolean hasTickets = true;
public void run() {
while (hasTickets) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
orderTicket();
}
}
public void orderTicket() {
if(ticketNums<=0) {//考虑的是没有票的情况
hasTickets = false;
return ;
}
synchronized(this) {
if(ticketNums<=0) {//考虑最后的1张票
hasTickets = false;
return ;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
}
-
编写两个线程,一个线程打印1-52的整数,另一个线程打印字母A-Z。打印顺序为12A34B56C….5152Z。即按照整数和字母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数52和字母Z结束。
要求:
-
编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。
-
在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,否则就输出i,使用notifyAll()进行唤醒其它线程。
-
在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待,否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。
-
编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。
-
编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。
-
编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程。
-
package week11_homework;
public class Printer {
private int index = 1;
public synchronized void print(int i) {
// 在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,
// 否则就输出i,使用notifyAll()进行唤醒其它线程。
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断index是否能被3整除
while (index % 3 == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i);
index++;
// 唤醒字母线程
this.notifyAll();
}
public synchronized void print(char c) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断index是否不能被3整除
while (index % 3 != 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.err.println(c);
index++;
// 唤醒其他线程
this.notifyAll();
}
public Printer(int index) {
super();
this.index = index;
}
public Printer() {
super();
}
}
package week11_homework;
public class NumberPrinter extends Thread {
private Printer p;
public NumberPrinter(Printer p) {
super();
this.p = p;
}
@Override
public void run() {
// 调用Printer类中的输出数字的方法。
for (int i = 1; i <= 52; i++) {
p.print(i);
}
}
}
package week11_homework;
public class LetterPrinter extends Thread {
private Printer p;
public LetterPrinter(Printer p) {
super();
this.p = p;
}
@Override
public void run() {
for(char c ='A';c<='Z';c++){
p.print(c);
}
}
}
package week11_homework;
public class PrinterTest {
public static void main(String[] args) {
Printer printer = new Printer();
new Thread(new NumberPrinter(printer)).start();
new Thread(new LetterPrinter(printer)).start();
}
}
- 编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。
package week11_homework;
public class CrossCave {
public static void main(String[] args) {
Cave cave = new Cave();
new Thread(cave,"小赵").start();
new Thread(cave,"小钱").start();
new Thread(cave,"小孙").start();
new Thread(cave,"小李").start();
new Thread(cave,"小周").start();
new Thread(cave,"小吴").start();
new Thread(cave,"小郑").start();
new Thread(cave,"小王").start();
new Thread(cave,"小冯").start();
new Thread(cave,"小陈").start();
}
}
class Cave implements Runnable {
private int count = 1;
@Override
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "正在第"+ count++ +"个过山洞");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}