面试01
java中有几种方法可以实现一个线程(jdk5.0之前)?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现Runnable接口。 用synchronized关键字修饰同步方法,反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。 suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志, 指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
面试02
sleep() 和 wait() 有什么区别? 答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
面试03
同步和异步有何异同,在什么情况下分别使用他们?举例说明。 答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
面试04
启动一个线程是用run()还是start()? 答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法就是正常的对象调用方法的执行,并不是使用分线程来执行的。
面试05
当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 答:不能,一个对象的一个synchronized方法只能由一个线程访问。
面试06
请说出你所知道的线程同步的方法。 答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
面试07
多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify
面试08
线程的基本概念、线程的基本状态以及状态之间的关系 答:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。 Java中的线程有四种状态分别是:创建、就绪、运行、阻塞、结束
面试09
简述synchronized和java.util.concurrent.locks.Lock的异同 ? 答:主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
面试10
实现三个线程间的通讯
面试11
一、判断题 1.C 和 Java 都是多线程语言。( ) 2.如果线程死亡,它便不能运行。( ) 3.在 Java 中,高优先级的可运行线程会抢占低优先级线程。( ) 4.程序开发者必须创建一个线程去管理内存的分配。( ) 5.一个线程在调用它的 start 方法,之前,该线程将一直处于出生期。( ) 6.当调用一个正在进行线程的 stop()方法时,该线程便会进入休眠状态。( ) 7.如果线程的 run 方法执行结束或抛出一个不能捕获的例外,线程便进入等待状态。( ) 8.一个线程可以调用 yield 方法使其他线程有机会运行。( ) 【答案】 1.难度:容易 答案:错误 知识点:C 是单线程语言。 2.难度:容易 答案:正确 知识点:线程死亡就意味着它不能运行。 3.难度:适中 答案:正确 知识点:线程优先级的使用。 4.难度:适中 答案:错误 知识点:Java 提供了一个系统线程来管理内存的分配。 5.难度:容易 答案:正确 知识点:出生期的概念。 6.难度:适中 答案:错误 知识点:应该是 sleep 方法。 7.难度:适中 答案:错误 知识点:如果线程的 run 方法执行结束或抛出一个不能捕获的例外,线程便进入死亡状态。 8.难度:适中 答案:正确 知识点:yield 方法总是让高优先级的就绪线程先运行。
面试12
二、选择题 1.Java 语言中提供了一个▁▁线程,自动回收动态分配的内存。 A 异步 B 消费者 C 守护 D 垃圾收集 2.当▁▁方法终止时,能使线程进入死亡状态。 A run B setPrority C yield D sleep 3.用▁▁方法可以改变线程的优先级。 A run B setPrority C yield D sleep 4.线程通过▁▁方法可以使具有相同优先级线程获得处理器。 A run B setPrority C yield D sleep 5.线程通过▁▁方法可以休眠一段时间,然后恢复运行。 A run B setPrority C yield D sleep 6.▁▁方法使对象等待队列的第一个线程进入就绪状态。 A run B notify C yield D sleep 7.方法 resume( )负责重新开始▁▁线程的执行。 A 被 stop( )方法停止 B 被 sleep( )方法停止 C 被 wait( )方法停止 D 被 suspend( )方法停止 8.▁▁方法可以用来暂时停止当前线程的运行。 A stop( ) B sleep( ) C wait( ) D suspend() 【答案】 1.难度:容易 答案:D 知识点:垃圾线程的使用。 2.难度:容易 答案:A 知识点:run 方法的使用。 3.难度:容易 答案:B 知识点:setPrority 方法的使用。 4.难度:容易 答案:C 知识点:yield 方法的使用。 5.难度:容易 答案:D 知识点:sleep 方法的使用。 6.难度:容易 答案:B 知识点:notify 方法的使用。 7.难度:适中 答案:D 知识点:一个线程被用 suspend( )方法,将该线程挂起。并通过调用 resume( )方法来重新开始线程的执行。 但是该方法容易导致死锁,应尽量避免使用。 8.难度:适中 答案:BCD 知识点:当调用 stop( )方法后,当前的线程不能重新开始运行。
面试13
Java为什么要引入线程机制,线程、程序、进程之间的关系是怎样的。 答:线程可以彼此独立的执行,它是一种实现并发机制的有效手段,可以同时使用多个线程来完成不同的任务,并且一般用户在使用多线程时并不考虑底层处理的细节。 程序是一段静态的代码,是软件执行的蓝本。进程是程序的一次动态执行过程,即是处于运行过程中的程序。 线程是比进程更小的程序执行单位,一个进程可以启动多个线程同时运行,不同线程之间可以共享相同的内存区域和数据。多线程程序是运行时间后嗣可能出现在一个进程之内的、有一个以上线程同时运行的情况的程序。
面试14
Runnable接口包括哪些抽象方法?Thread类有哪些主要域和方法? 答:Runnable接口中仅有run()抽象方法。 Thread类主要域有:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY。 主要方法有start(),run(),sleep(),currentThread(),setPriority(),getPriority(),join()等。
面试15
创建线程有哪两种方式(jdk5.0之前)?试写出每种的具体的流程。比较两种创建方式的不同,哪个更优。 1—继承Thread类 1) 定义类继承Thread类。 2) 覆盖Thread类中的run方法。 3) 创建Thread子类对象,即创建了线程对象。 4) 调用线程对象start方法:启动线程,调用run方法。 2—实现Runnable接口 1)定义类,实现Runnable接口。 2)覆盖Runnable接口中的run方法。 3)通过Thread类建立线程对象。 4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中。 5)调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。 【区别】 继承Thread: 线程代码存放Thread子类run方法中。 实现Runnable:线程代码存在接口的子类的run方法。 【实现方法的好处】 1)避免了单继承的局限性 2)多个线程可以共享同一个接口子类的对象,非常适合多个相同线程来处理同一份资源。
面试16
/* 编写一个继承Thread类的方式实现多线程的程序。该类MyThread有两个属性,一个字符串WhoAmI代表线程名,一个整数delay代表该线程随机要休眠的时间。构造有参的构造器,线程执行时,显示线程名和要休眠时间。 另外,定义一个测试类TestThread,创建三个线程对象以展示执行情况。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 10:02 */ public class Exer9 { public static void main(String[] args) { MyThread myThread1 = new MyThread("线程一", (int) (Math.random() * 100)); MyThread myThread2 = new MyThread("线程二", (int) (Math.random() * 100)); MyThread myThread3 = new MyThread("线程三", (int) (Math.random() * 100)); myThread1.start(); myThread2.start(); myThread3.start(); } } class MyThread extends Thread { String name; int delay; public MyThread() { } public MyThread(String name, int delay) { this.name = name; this.delay = delay; } @Override public void run() { try { Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("当前线程:" + Thread.currentThread().getName() + ",延迟了:" + delay); } }
面试17
在{1}添加什么代码,可以保证如下代码输出100 提示:t.wait() 或 t.jion() 或 t.yield() 或 t.interrupt()? public class Test { public static void main(String[] args) { MyThread m = new MyThread(); Thread t = new Thread(m); t.start(); {1} int j = m.i; System.out.println(j); } } class MyThread implements Runnable{ int i; public void run(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i=100; } } 答案:t.join()
面试18
利用多线程设计一个程序,同时输出 50 以内的奇数和偶数,以及当前运行的线程名。
public class ThreadPrint extends Thread { int k = 1; public void run() { int i = k; while (i < 50) { System.out.println(Thread.currentThread().getName() + "-----" + i); i += 2; } System.out.println(Thread.currentThread().getName() + " end!"); } public static void main(String[] args) { ThreadPrint t1 = new ThreadPrint(); ThreadPrint t2 = new ThreadPrint(); t1.k = 1; t2.k = 2; t1.start(); t2.start(); } }
面试19
建立线程有几种方法?
面试20
/* 定义两个线程(一个用继承Thread类,一个用实现Runnable接口),定义一个测试类包括一个主函数调用两个子线程(具体实现自定) */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-02 19:43 */ public class Exer1 { public static void main(String[] args) { MyThread1 myThread1 = new MyThread1(); myThread1.start(); MyThread2 myThread2 = new MyThread2(); new Thread(myThread2).start(); } } class MyThread1 extends Thread { @Override public void run() { System.out.println("继承Thread类。。。"); } } class MyThread2 implements Runnable { @Override public void run() { System.out.println("实现Runnable接口..."); } }
面试21
/* 模拟一个人生产50个玩具,每200毫秒生产一个,当生产到第20个时加入每秒吃1个馒头,共吃完3个后在接着生产的多线程。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 8:29 */ public class Exer2 { public static void main(String[] args) { Person person = new Person(); new Thread(person).start(); } } class Person implements Runnable{ int toys=0; @Override public void run() { while (true){ if (toys<=50){ synchronized (this){ // notify(); try { System.out.println("生产第"+toys+"玩具"); toys++; Thread.sleep(200); if (toys==20){ // wait(); for (int i = 0; i < 3; i++) { System.out.println("开始吃馒头。。。"); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } } }else { break; } } } }
面试22
/* 编写龟兔赛跑多线程程序,设赛跑长度为100米,每跑完10米输出一次结果。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 8:42 */ public class Exer3 { /* 编写龟兔赛跑多线程程序,设赛跑长度为100米,每跑完10米输出一次结果。 */ public static void main(String[] args) { Rabbit rabbit = new Rabbit(); Turtle turtle = new Turtle(); Thread thread1 = new Thread(rabbit); thread1.setName("兔子"); Thread thread2 = new Thread(turtle); thread2.setName("乌龟"); thread1.start(); thread2.start(); } } class Rabbit implements Runnable { int length = 100; @Override public void run() { while (true) { if (length >= 0) { synchronized (this) { notify(); System.out.println("兔子开始跑,距离终点还剩了:" + length + "米"); length--; try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } if (length % 10 == 0) { System.out.println("跑了10米,还剩:" + length + "米"); } } } else { break; } } } } class Turtle implements Runnable { int length = 100; @Override public void run() { while (true) { if (length >= 0) { synchronized (this) { notify(); System.out.println("乌龟开始跑,距离终点还剩了:" + length + "米"); length--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (length % 10 == 0) { System.out.println("跑了10米,还剩:" + length + "米"); } } } else { break; } } } }
面试23
/* 改进上题的龟兔赛跑程序,通过改变优先级,并减掉休眠时间,使得乌龟以迅雷不及掩耳的速度跑完100米。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 8:58 */ public class Exer4 { /* 改进上题的龟兔赛跑程序,通过改变优先级,并减掉休眠时间,使得乌龟以迅雷不及掩耳的速度跑完100米。 */ public static void main(String[] args) { Rabbit1 rabbit1 = new Rabbit1(); Turtle1 turtle1 = new Turtle1(); Thread thread1 = new Thread(rabbit1); Thread thread2 = new Thread(turtle1); thread1.setName("兔子"); thread2.setName("乌龟"); thread2.setPriority(Thread.MAX_PRIORITY);//改变乌龟的优先级 thread1.start(); thread2.start(); } } class Rabbit1 implements Runnable { int length = 100; @Override public void run() { while (true) { if (length >= 0) { synchronized (this) { // notify(); System.out.println(Thread.currentThread().getName() + "开始跑,距离终点还剩了:" + length + "米"); length--; // try { // Thread.sleep(20); // } catch (InterruptedException e) { // e.printStackTrace(); // } if (length % 10 == 0) { System.out.println("跑了10米,还剩:" + length + "米"); } } } else { break; } } } } class Turtle1 implements Runnable { int length = 100; @Override public void run() { while (true) { if (length >= 0) { synchronized (this) { // notify(); System.out.println(Thread.currentThread().getName() + "开始跑,距离终点还剩了:" + length + "米"); length--; // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // if (length%10==0){ // System.out.println("跑了10米,还剩:"+length+"米"); // } } } else { break; } } } }
面试24
在多线程中,为什么要引入同步机制?
面试25
/* 启动两个线程对一个数字i操作 1) 其中1个线程每次对i加1 2) 另1个线程每次对i减1 各运行20次,结果i的值等于初始值。 */
面试26
wait()、notify()、notifyAll()的作用分别是什么?
面试27
/* 实现一个由A、B、C三个窗口同时销售100张票的系统,要求打印出每个窗口打印的售票情况,并且每个窗口不得连续售票。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 9:24 */ public class Exer6 { public static void main(String[] args) { Windows windows = new Windows(); Thread thread1 = new Thread(windows); Thread thread2 = new Thread(windows); Thread thread3 = new Thread(windows); thread1.setName("一号窗口"); thread2.setName("二号窗口"); thread3.setName("三号窗口"); thread1.start(); thread2.start(); thread3.start(); } } class Windows implements Runnable{ int ticket = 100; @Override public void run() { while (true){ if (ticket>=0){ synchronized (this){ notifyAll(); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + ticket--); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }else { break; } } } }
面试28
/* 模拟3个人排队买票,每人买1张票。售货员只有1张五元的钱,电影票5元一张,王大拿拿一张二十元的人民币排在谢大脚前面买票,谢大脚拿1张十元的人民币排在在赵四的前面买票,赵四拿1张五元的人民币排在最后。即最终的卖票次序是:谢大脚、赵四、王大拿 */
面试29
/* 编写生产者消费者多线程程序,设有一个最大库存量为4的电视机仓库,生产10台电视机,一边生产一边销售(消费)。 */
package com.jerry.exer1; /** * @author jerry_jy * @create 2022-10-03 9:37 */ public class Exer8 { public static void main(String[] args) { Factory factory = new Factory(); Producer producer = new Producer(factory); Consumer consumer = new Consumer(factory); Thread thread1 = new Thread(producer); Thread thread2 = new Thread(consumer); thread1.start(); thread2.start(); } } class Factory { int TV = 0; final int MAX_AMOUNT = 4; public synchronized void addTV() { while (TV <= 10) { if (TV > MAX_AMOUNT) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { TV++; System.out.println("生产者生产了第" + TV + "个产品"); notifyAll(); } } } public synchronized void getTV() { while (TV <= 10) { if (TV <= 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println("消费者取走了第" + TV + "个产品"); TV--; notifyAll(); } } } } class Producer implements Runnable { Factory factory; public Producer(Factory factory) { this.factory = factory; } @Override public void run() { System.out.println("生产者开始生产产品"); while (true) { try { Thread.sleep((int) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } factory.addTV(); } } } class Consumer implements Runnable { Factory factory; public Consumer(Factory factory) { this.factory = factory; } @Override public void run() { System.out.println("生产者开始生产产品"); while (true) { try { Thread.sleep((int) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } factory.getTV(); } } }