Java面试——多线程与并发

2020-03-12  本文已影响0人  抬头挺胸才算活着

CAS的原理

AQS等待队列为什么设计成双向链表?

juc包下知道哪些类?

AQS,ReentrantLock,ReentrantReadWriteLock,Semaphore,CountdownLatch,ConcurrentHashMap,CopyOnWriteArrayList


线程的sleep()方法和yield()方法有什么区别?

①sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

线程的基本状态以及状态之间的关系

注意阻塞跟等待,在Sychronize中就有阻塞跟等待,等待锁的时候是阻塞,调用wait的时候是等待。

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

  3. 阻塞(BLOCKED):表示线程阻塞于锁。

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  6. 终止(TERMINATED):表示该线程已经执行完毕。


  7. 在 java 程序中怎么保证多线程的运行安全?
    线程安全在三个方面体现:
    原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
    可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
    有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

  8. 多线程锁的升级原理是什么?
    在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。
    锁升级的图示过程:


    图片.png

交替输出

三个线程分别输出'a','l','i',输出'a'的线程控制输出的次数。

public class main {
    private volatile static boolean hasAPrint = false;
    private volatile static boolean hasLPrint = true;
    private volatile static boolean hasIPrint = true;

    public static void main(String[] args) {

        Object lock = new Object();

        new Thread(()->{
            for(int i=0;i<5;i++) {
                synchronized (lock) {
                    while (!hasIPrint) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("a");
                    hasAPrint = true;
                    hasIPrint = false;
                    lock.notifyAll();
                }
            }
        }).start();

        new Thread(()->{
            while (true) {
                synchronized (lock) {
                    while (!hasAPrint) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("l");
                    hasLPrint = true;
                    hasAPrint = false;
                    lock.notifyAll();
                }
            }

        }).start();

        new Thread(()->{
            while (true) {
                synchronized (lock) {
                    while (!hasLPrint) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print("i");
                    hasIPrint = true;
                    hasLPrint = false;
                    lock.notifyAll();
                }
            }
        }).start();
}

大部分的代码都是一样的,下面是精简版本。
总结来说,这是同步,一个做完另外一个才接着做。

public class Printer {
    private volatile int times;
    private volatile int completeCondition;

    public Printer(int times, int completeCondition) {
        this.times = times;
        this.completeCondition = completeCondition;
    }

    public void print(int waitCondition, int completeCondition, String string){
        for (int i = 0; i < times; i++) {
            synchronized (this){
                while(this.completeCondition!=waitCondition){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(string);
                this.completeCondition = completeCondition;
                this.notifyAll();
            }
        }
    }

    private volatile static Printer printer = new Printer(5, 0);

    public static void testPrint() {
        new Thread(()->{
            printer.print(0, 1, "a");
        }).start();

        new Thread(()->{
            printer.print(1, 2, "l");
        }).start();

        new Thread(()->{
            printer.print(2, 0, "i");
        }).start();
    }
}

线程池

对于一个普通的线程池,coreSize = 5, maxSize = 10,阻塞队列长度 20,且插入线程是永久执行的,那么不断插入线程,线程池中的数量以及对应的反应如何?
注意无法加入队列的时候再创建线程


上一篇下一篇

猜你喜欢

热点阅读