多线程面试题

145 阅读2分钟

1 System.out.println 导致线程停止

public class ThreadStop {
    public  static boolean stop;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int i =0;
                while (!stop) {
                    i++;
                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        stop = true;
    }
}

如上代码,我们都知道,因为 stop的值更新在主线程,子线程的值不是最新的,所以会循环不止,但是我们加上一行代码再试试

public class ThreadStop {
    public  static boolean stop;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int i =0;
                while (!stop) {
                    i++;
                    System.out.println(" i的值 "+i);

                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        stop = true;
    }
}

我们会发现线程停止了,问什么?有人说是System.out.println 源码里有synchronized 关键字,会改变stop 的值,其实不然,Jvm的CPU会尽力保证变量值的更新,但是它不是100%成功的,只能尽自己能力,如果CPU一直没有空闲出来,它就不会进行更新,但是一旦CPU空闲了,就有机会去更新。所以关键是让CPU获取时间片,另外我们也可以在while 中进行 sleep,也可以实现线程终止

2 如何理解 sleepwait

首先我们提几个问题 1.sleep 会放弃线程占有的锁吗? 2. wait 调用之前需要占有对象的锁吗? 如果需要, namewait之后会放弃对象占有的锁吗?

2.1 等待池 和 锁池

先介绍2个概念

  • 锁池:当多个线程竞争对象 A 某个synchronized 方法时,只有一个线程会占有锁,其他线程都需要等待,这时候等待的线程就会进入对象A 的 锁池
  • 等待池:假如线程 T 调用了线程 Await方法,T 就会释放该对象的锁,同时进入对象A的等待池,如果其他对象notify 或者notifyAll被调用,name对象A的等待池中的线程(1个或者全部,取决于notify 还是notifyAll)会进入 锁池,伺机争夺锁的拥有权

所以开始的答案是 1.sleep 不会放弃锁,即便休眠仍然持有 2. 只有占有锁的线程,才有机会调用 对象的wait方法,调用之后,将会放弃其占有锁,并进入等待池,如果一个没有占有锁的线程调用 wait 会抛出异常