多线程作业
一、 填空题
- 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入___阻塞______状态。
- 处于新建状态的线程被启动后,将进入线程队列排队等待CPU,此时它已具备了运行条件,一旦轮到享用CPU资源就可以获得执行机会。上述线程是处于 就绪 状态。
- 一个正在执行的线程可能被人为地中断,让出CPU的使用权,暂时中止自己的执行,进入 阻塞 状态。
- 在Java中编写实现多线程应用有两种途径:一种是继承Thread类创建线程,另一种是实现 Runnable 接口创建线程。
- 在线程控制中,可以调用_____join()_______方法,阻塞当前正在执行的线程,等插队线程执行完后后再执行阻塞线程。
- 多线程访问某个共享资源可能出现线程安全问题,此时可以使用____synchronized____________关键字来实现线程同步,从而避免安全问题出现,但会影响性能,甚至出现死锁。
- 在线程通信中,调用wait( )可以是当前线程处于等待状态,而为了唤醒一个等待的线程,需要调用的方法是___notify()___________。
- 在线程通信中,可以调用wait()、notify()、notifyAll()三个方法实现线程通信,这三个方法都是______Object________类提供的public方法,所以任何类都具有这三个方法。
二、 选择题
1. |
下列关于Java线程的说法正确的是( A )。(选择一项) |
|
|
|
|
|
A |
每一个Java线程可以看成由代码、一个真实的CPU以及数据三部分组成 |
|
B. |
创建线程的两种方法中,从Thread类中继承方式可以防止出现多父类的问题 |
|
C. |
Thread类属于java.util程序包(java.lang.Thread) |
|
D. |
使用new Thread(new X()).run();方法启动一个线程 |
2. |
以下选项中可以填写到横线处,让代码正确编译和运行的是( 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(); |
3. |
如下代码创建一个新线程并启动线程,问:四个选项中可以保证正确代码创建target对象,并能编译正确的是( C )?(选择一项) |
|
|
public static void main(String[] args) { Runnable target=new MyRunnable( ); Thread myThread=new Thread(target); } |
|
|
|
|
|
A |
public class MyRunnable extends Runnable { public void run( ) {
} } |
|
B. |
public class MyRunnable extends Runnable { void run( ) {
} } |
|
C. |
public class MyRunnable implements Runnable { public void run( ) {
} } |
|
D. |
public class MyRunnable implements Runnable { void run( ) { } } |
4. |
当线程调用start( )后,其所处状态为( C )。(选择一项) |
|
|
|
|
|
A |
阻塞状态 |
|
B. |
运行状态 |
|
C. |
就绪状态 |
|
D. |
新建状态 |
5. |
下列关于Thread类提供的线程控制方法的说法中,错误的是(C )。(选择一项) |
|
|
|
|
|
A |
线程A中执行线程B的join()方法,则线程A等待直到B执行完成 |
|
B. |
线程A通过调用interrupt()方法来中断其阻塞状态 |
|
C. |
若线程A调用方法isAlive()返回值为false,则说明A正在执行中,也可能是可运行状态 |
|
D. |
currentThread()方法返回当前线程的引用 |
6. |
下列关于线程的优先级说法中,正确的是( B C )。(选择两项) |
|
|
|
|
|
A |
线程的优先级是不能改变的 |
|
B. |
线程的优先级是在创建线程时设置的 |
|
C. |
在创建线程后的任何时候都可以重新设置 |
|
D. |
线程的优先级的范围在1-100之间 |
7. |
以下选项中关于Java中线程控制方法的说法正确的是( A D )。(选择二项) |
|
|
|
|
|
A. |
join ( ) 的作用是阻塞指定线程等到另一个线程完成以后再继续执行 |
|
B. |
sleep ( ) 的作用是让当前正在执行线程暂停,线程将转入就绪状态 阻塞 |
|
C. |
yield ( ) 的作用是使线程停止运行一段时间,将处于阻塞状态 就绪 |
|
D. |
setDaemon( )的作用是将指定的线程设置成后台线程 |
8. |
在多个线程访问同一个资源时,可以使用( A )关键字来实现线程同步,保证对资源安全访问。(选择一项) |
|
|
|
|
|
A. |
synchronized |
|
B. |
transient |
|
C. |
static |
|
D. |
yield |
9. |
Java中线程安全问题是通过关键字( C )解决的?。(选择一项) |
|
|
|
|
|
A. |
finally |
|
B. |
wait( ) |
|
C. |
synchronized |
|
D. |
notify( ) |
10. |
以下说法中关于线程通信的说法错误的是( D)?。(选择一项) |
|
|
|
|
|
A. |
可以调用wait()、notify()、notifyAll()三个方法实现线程通信 |
|
B. |
wait()、notify()、notifyAll()必须在synchronized方法或者代码块中使用 |
|
C. |
wait()有多个重载的方法,可以指定等待的时间 |
|
D. |
wait()、notify()、notifyAll()是Object类提供的方法,子类可以重写 |
三、 判断题
- 进程是线程Thread内部的一个执行单元,它是程序中一个单一顺序控制流程。( F )
- 一个进程可以包括多个线程。两者的一个主要区别是:线程是资源分配的单位,而进程CPU调度和执行的单位。( F )
- 用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态。( T )
- 线程可以用yield使低优先级的线程运行。( T )
- Thread.sleep( )方法调用后,当等待时间未到,该线程所处状态为阻塞状态。当等待时间已到,该线程所处状态为运行状态。( F ) 就绪
- 当一个线程进入一个对象的一个synchronized方法后,其它线程不可以再进入该对象同步的其它方法执行。( T )
四、 简答题
- 简述进程和线程的联系和区别。
进程:一个程序对一个数据集的动态执行过程,是分配资源的基本单位,是资源分配的最小单位
线程:一个进程内的基本调度单位, CPU调度和执行的单位,是程序执行的最小单位
区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
- 创建线程的两种方式分别是什么?各有什么优缺点
第一种方式:使用Runnable接口创建线程,重写run方法
第二种方式:直接继承Thread类创建对象,重写run方法
Runnable:接口方案的优点:降低类与类的耦合性,缺点是书写复杂
Thread继承方案的优点:书写简单,缺点是耦合性太高
- 请你简述sleep( )和wait( )有什么区别?
(1)同步锁的对待不同:
sleep()后,程序并不会不释放同步锁。
wait()后,程序会释放同步锁。
(2)用法的不同:
sleep()可以用时间指定来使他自动醒过来。如果时间不到你只能调用interreput()来强行打断。
wait()可以用notify()直接唤起。
- Java中实现线程通信的三个方法及其作用。
在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信。
作用:当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
1 一、 编码题 2 1. 设计一个多线程的程序如下:设计一个火车售票模拟程序。假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况。 3 4 public class test_1 { 5 public static void main(String[] args) { 6 //创建1个资源共享类 7 TrainTickets tr = new TrainTickets(); 8 //创建5个售票点多线程,并且给多线程起名 9 Thread t1 = new Thread(tr, "售票点1"); 10 Thread t2 = new Thread(tr, "售票点2"); 11 Thread t3 = new Thread(tr, "售票点3"); 12 Thread t4 = new Thread(tr, "售票点4"); 13 Thread t5 = new Thread(tr, "售票点5"); 14 //启动多线程 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 t4.start(); 19 t5.start(); 20 } 21 } 22 public class TrainTickets implements Runnable { 23 int tickets = 100; 24 @Override 25 public void run() { 26 while(true){ 27 sellTickets(); 28 } 29 } 30 public synchronized void sellTickets(){ 31 if(tickets>0){ 32 try { 33 Thread.sleep(10); 34 System.out.println(Thread.currentThread().getName()+":正在出售"+(tickets--)+"张票!"); 35 } catch (InterruptedException e) { 36 e.printStackTrace(); 37 } 38 } 39 } 40 } 41 42 43 2. 编写两个线程,一个线程打印1-52的整数,另一个线程打印字母A-Z。打印顺序为12A34B56C….5152Z。即按照整数和字母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数52和字母Z结束。 44 要求: 45 1) 编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。 46 2) 在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,否则就输出i,使用notifyAll()进行唤醒其它线程。 47 3) 在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待,否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。 48 4) 编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。 49 5) 编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。 50 6) 编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程。 51 52 public class Printer { 53 /** 54 *1)编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。 55 * 2) 在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待, 56 * 否则就输出i,使用notifyAll()进行唤醒其它线程。 57 * 3) 在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待, 58 * 否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。 59 */ 60 private int index = 1; 61 //打印数字的方法print(int i) 62 public synchronized void print(int i){ 63 if(index%3==0){ 64 try { 65 wait(); 66 } catch (InterruptedException e) { 67 e.printStackTrace(); 68 } 69 } 70 System.out.print(i); 71 index++; 72 notifyAll(); 73 } 74 public synchronized void print(char c){ 75 if(index%3!=0){ 76 try { 77 wait(); 78 } catch (InterruptedException e) { 79 e.printStackTrace(); 80 } 81 } 82 System.out.print(c); 83 index++; 84 notifyAll(); 85 } 86 } 87 public class NumberPrinter extends Thread{ 88 /** 89 * 4)编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p; 90 * 在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。 91 */ 92 private Printer p; 93 94 public NumberPrinter(Printer p) { 95 this.p = p; 96 } 97 @Override 98 public void run() { 99 for(int i=1;i<53;i++){ 100 p.print(i); 101 } 102 } 103 } 104 public class LetterPrinter extends Thread{ 105 /** 106 * * 5)编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p; 107 * 在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。 108 */ 109 private Printer p; 110 111 public LetterPrinter(Printer p) { 112 this.p = p; 113 } 114 @Override 115 public void run() { 116 for(char j='A';j<='Z';j++){ 117 p.print(j); 118 } 119 } 120 } 121 public class test_2 { 122 /** 123 * 6) 编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程。 124 */ 125 public static void main(String[] args) { 126 //创建公共打印资源对象 127 Printer p = new Printer(); 128 //创建数字打印线程 129 Thread t1 = new NumberPrinter(p); 130 //创建字母打印线程 131 Thread t2 = new LetterPrinter(p); 132 //启动线程 133 t1.start(); 134 t2.start(); 135 } 136 } 137 138 二、 可选题 139 1. 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。 140 要求:使用内部类实现线程,对j增减的时候不考虑顺序问题。 141 public class test_3 { 142 public static void main(String[] args) { 143 //创建公共资源类 144 public_3 p =new public_3(); 145 //创建自加线程,并命名 146 Thread t1 = new appded(p); 147 t1.setName("自增"); 148 //创建自减线程,并命名 149 Thread t2 = new subtract(p); 150 t2.setName("自减"); 151 //启动线程 152 t1.start(); 153 t2.start(); 154 } 155 } 156 public class public_3 { 157 //+1 158 private int j = 1; 159 public synchronized void appded(){ 160 j++; 161 System.out.println(Thread.currentThread().getName()+":j++="+j); 162 } 163 public synchronized void subtract(){ 164 j--; 165 System.out.println(Thread.currentThread().getName()+":j--="+j); 166 } 167 } 168 public class appded extends Thread{ 169 public_3 p = new public_3(); 170 171 public appded(public_3 p) { 172 this.p = p; 173 } 174 @Override 175 public void run() { 176 for (int i = 1; i < 10; i++) { 177 p.appded(); 178 } 179 } 180 } 181 public class subtract extends Thread { 182 public_3 p =new public_3(); 183 public subtract(public_3 p) { 184 this.p = p; 185 } 186 @Override 187 public void run() { 188 for (int i = 1; i < 10; i++) { 189 p.subtract(); 190 } 191 } 192 } 193 2. 编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。 194 195 public class test_4 { 196 public static void main(String[] args) { 197 //创建山洞线程 198 Hole h = new Hole(); 199 //创建10个人的线程 200 Thread t1 = new Thread(h,"秦始皇"); 201 Thread t2 = new Thread(h,"刘邦"); 202 Thread t3 = new Thread(h,"刘策"); 203 Thread t4 = new Thread(h,"杨坚"); 204 Thread t5 = new Thread(h,"李世民"); 205 Thread t6 = new Thread(h,"赵匡胤"); 206 Thread t7 = new Thread(h,"成吉思汗"); 207 Thread t8 = new Thread(h,"朱元璋"); 208 Thread t9 = new Thread(h,"康熙"); 209 Thread t10 = new Thread(h,"蒋介石"); 210 t1.start(); 211 t2.start(); 212 t3.start(); 213 t4.start(); 214 t5.start(); 215 t6.start(); 216 t7.start(); 217 t8.start(); 218 t9.start(); 219 t10.start(); 220 221 } 222 223 } 224 public class Hole extends Thread{ 225 private static int i=1; 226 @Override 227 public void run() { 228 //上锁 229 synchronized(this){ 230 231 System.out.println("第"+i+"个人"+Thread.currentThread().getName()+"正在通过山洞"); 232 try { 233 sleep(100); 234 i++; 235 } catch (InterruptedException e) { 236 // TODO Auto-generated catch block 237 e.printStackTrace(); 238 } 239 240 } 241 } 242 243 }