关于 Java 中多线程的面试问题 详解


多线程细节:

1. 面试题: sleep 方法 和 wait 方法异同点是什么?

相同点: 可以让线程 处于 冻结状态.

不同点:

1.
sleep 必须指定时间
wait 可以指定时间, 也可以不指定时间.

2.

sleep 时间到 线程会处于 临时阻塞 或者运行.

wait 如果没有指定时间 , 必须要通过 notify 或者 notifyAll 唤醒.

3.

sleep 不一定非要定义在 同步中.

wait 必须要定义在同步中. (锁)

4.

都定义在同步中,

线程执行到 sleep, 不会释放锁. (因为 sleep 肯定能醒)

线程执行到 wait, 会释放锁. (wait 就 不一定 能醒)

5.

方法名不相同


// *** 此时的同步代码块中 有多个线程, 不会 出现 线程安全 问题.

// *** 因为里面 的线程 光有执行权是不够的, 还得 需要锁.

synchronized(obj)
{
// sleep(5000); // *** 5000 毫秒, 也就是 5 秒. // *** 释放执行权,

wait(); // *** 0 1 2 // *** 线程池. 这三个 线程 一旦 被唤醒, 都会处于 临时阻塞 状态.

code...

}

synchronized(obj){


notifyAll(); // *** 3 // *** 持有锁.

code...


// *** 释放锁 对于同步代码块 而言 是 隐式的, 会在 同步代码块的最后 释放锁.
}

 

 

2. 线程 如何 停止呢?

扫描二维码关注公众号,回复: 7764965 查看本文章

stop 方法 过时了, 看其 描述发现, 有其它 解决方案.

线程结束: 就是让 线程任务代码 执行完, run 方法结束.

 

run 方法 咋结束呢?

run 方法中 通常都定义 循环, (如果没有定义循环 就不要 开启 多线程了, 没有意义.), 只要 控制循环 就 哦了.





*** 注意: 万一线程 在 任务中 处于了 冻结状态, 那么 它 还能去判断标记吗? 不能!

咋办? 答: *** 通过 查阅 stop 方法的描述, 发现提供了一个 解决方法, 如果目标线程 等待很长时间, 则 应使用 interrupt 方法来 中断等待.


所谓的 中断 并不是 停止线程.

*** interrupt 功能是 将线程 的 冻结状态 清除, 让线程 恢复到 运行状态(让 线程 重新 具备 CPU 的执行资格).

*** interrupt 让线程 从 冻结状态 恢复 回来. 清除 其 中断 状态.



*** 因为是 强制性的, 所以会有 异常发生, 可以在 catch 中 捕获异常, *** 在 异常处理中, 改变标记 让 循环结束, 让 run 方法结束.

 

 

3. 守护线程: 后台线程, 一般创建的 都是前台线程。

前台 后台 线程运行时 都是一样的, 获取 CPU 的执行权执行。

只有结束的时候有些不同。

前台线程要通过 run 方法结束, 线程结束。

后台线程 也可以通过 run 方法结束, 线程结束, 还有另一种情况,

当进程中 所有的前台线程都 结束了, 这时 无论后台线程 处于什么样的状态, 都会结束 , 从而 进程结束。

进程结束 依赖的 都是 前台线程。


之所以这里称之为 守护线程, 无非是 后台线程 在 守护着 前台线程。

前台 线程们结束了, 后台 线程 就自动跟着 结束了。


4. 线程 的 优先级: 是 用 数字标识的: 1-10

其中默认 初始优先级 是 5。 最明显的 三个 优先级 1, 5, 10

setPriority(10); // *** 设置线程 的优先级。

setPriority(Thread.MAX_PRIORITY);

setPriority(Thread.MIN_PRIORITY);

setPriority(Thread.NORM_PRIORITY);


5. 线程组: ThreadGroup: 可以通过 Thread 的 构造函数 明确 新 线程对象 所属的 线程组。

线程组的好处: 可以对 多个 同组的线程, 进行 统一的操作。

默认都属于 main 线程组。


6. 线程 是可以 自己 取名字的。

例如:

Thread t1 = new Thread(d,"旺财"); // *** 旺财 线程名

Thread t2 = new Thread(d,"小强"); // *** 小强 线程名

可以自己 给 线程 取名 以 增强 程序的 阅读性。

例如:

输入线程, 运行线程 etc.


7. join 方法

主线程执行到这里, 知道 t1 要加入 执行, 主线程 释放了 执行权、 执行资格 并处于 冻结状态, 什么时候恢复呢?

等 t1 线程 执行完。
try{t1.join();}catch(InterruptedException e){}


try{t1.join();}catch(InterruptedException e){} // *** 一般这个方法 用于 临时 加入一个 运算的线程, 让 该 线程 运算完, 程序才会继续执行。

 


8. yield(); (static 静态的)

// *** 暂停 当前正在 执行的线程对象, 并执行 其它 线程。

Thread.yield(); // *** 线程 临时暂停。 将执行权 释放, 让其他 线程 有机会 获取 执行权。

 


// *** 嵌套类 也就是 内部类。

 

9. 开发中, 线程 匿名内部类 体现。

// *** 面试题

new Thread(new Runnable() // *** 就是 往里面 传递了 一个 线程任务 对象。 在 执行的时候, 就按照 这个对象的 任务来执行。
{
public void run(){
}


}).start();

 

 

// *** 面试题

new Thread(new Runnable() // *** 就是 往里面 传递了 一个 线程任务对象。 在 执行的时候, 就按照 这个对象的 任务来执行。
{
public void run(){

System.out.println("runnable run");
}


}){

public void run(){


System.out.println("subthread run");
}

}.start();

}

 

  1 public class ThreadTest{
  2     
  3     public static void main(String[] args) {
  4         
  5     /*
  6     // *** 使用 匿名内部类的方式 创建一个 线程。    
  7         new Thread(){ // *** 这是一个 Thread 的子类对象, 可以 进行 覆盖 run 方法的操作。
  8             
  9             public void run(){
 10                 
 11                 for(int x = 0; x < 40; x++){
 12                     System.out.println(Thread.currentThread().getName() +"....X...." + x);
 13                 }
 14         }
 15         
 16 }.start();
 17         
 18         
 19     Runnable r = new Runnable(){
 20         
 21         public void run(){
 22             
 23             for(int x = 0; x < 40; x++){
 24                 System.out.println(Thread.currentThread().getName() +"....Y...." + x);
 25             }
 26             
 27         }
 28         
 29     };
 30     
 31     new Thread(r).start();
 32 
 33 
 34         
 35         for(int x = 0; x < 40; x++){
 36             System.out.println(Thread.currentThread().getName() +"....Z...." + x);
 37         }
 38         
 39         */
 40         
 41         // *** 面试题
 42         
 43         new Thread(new Runnable() // *** 就是 往里面 传递了 一个 线程任务对象。 在 执行的时候, 就按照 这个对象的 任务来执行。
 44         {
 45             public void run(){    
 46                 
 47                 System.out.println("runnable run");
 48             }
 49             
 50             
 51         }){
 52             
 53             public void run(){
 54                 
 55                 
 56                 System.out.println("subthread run");   // *** 执行
 57             }
 58             
 59         }.start();  // new  Thread 后面是一个 大括号, 就是一个 子类.
 60         
 61     }
 62 //***  子类 覆盖 父类,  运行的 时候 运行的 是 子类 方法.
 63 
 64 }
 65 
 66 
 67 
 68 /*
 69 
 70 class Thread{
 71     
 72     private Runnable r;
 73     Thread(Runnable r)
 74     {
 75         
 76         this.r = r;
 77         
 78     }
 79     
 80     public void run(){
 81         
 82         
 83         if(r != null){
 84             
 85             
 86             r.run(); // *** 这里调用的 是 Runnable 的 run 方法.
 87             
 88         }
 89         
 90     }
 91     
 92     public void start(){
 93         
 94         run();
 95         
 96         
 97     }
 98     
 99 }
100 
101 class SubThread  extends Thread{  // *** 子类 覆盖 父类,  运行的 时候 运行的 是 子类 方法.
102     
103     public void run(){
104         
105         
106         System.out.pritnln("subthread run");
107     }
108     
109     
110 }
111 
112 
113 Runnable r = new Runnable();
114 {
115     
116         public void run(){
117             
118             
119             System.out.println("runnable run");
120             
121         }
122     
123 }
124 //Thread t = new Thread(r);
125 
126 SubThread t = new SubThread(r);
127 
128 t.start();
129 
130 
131 // *** 这里先 调用 start  方法, 然后 调用 run 方法, 然后 判断  r  不为空, 然后 调用 Runnable 的 run 方法(因为 传进去的 参数 是 Runnable 的 对象 r), 然后 输出 runnable run.
132 
133 
134 */

 




 

 

 

 

 




猜你喜欢

转载自www.cnblogs.com/CYjiang/p/11805236.html