多线程面试题2

1.如何预防死锁?

多线程面试题2_java
多线程面试题2_信号量_02

2.描述一下线程安全活跃态问题,竞态条件?

多线程面试题2_信号量_03
多线程面试题2_java_04
多线程面试题2_信号量_05
两个或多个线程同时对一共享数据进行修改,从而影响程序运行的正确性时,这种就被称为竞态条件(race condition)

3.描述一下进程与线程区别?

多线程面试题2_信号量_06

4.程序开多少线程合适?

多线程面试题2_数据_07

5.描述一下notify和notifyAll区别?

首先最好说一下池锁和等待池的概念
多线程面试题2_信号量_08
然后再来说notify和notifyAll的区别
多线程面试题2_信号量_09
多线程面试题2_多线程_10

6.多线程之间是如何通信的?

多进程之间通信:信号量、信号、套接字、有名管道、无名管道、共享内存、mq
由于多线程之间共享了进程的地址空间,所以,原生就支持数据共享,当一个线程修改了进程的变量,由于共享空间,另外一个线程自然能看到,所以原生支持通信。由于线程之间并发必然会引起互斥操作,这时就需要同步机制:
volatile、sync、ReentrantLock、ReadWriteReenTrantLock

7.重入锁的实现原理

多线程面试题2_数据_11
多线程面试题2_java_12
多线程面试题2_多线程_13
多线程面试题2_java_14

8.Volatile原理

多线程面试题2_java_15
至于是怎么发现数据是否失效呢?
多线程面试题2_数据_16
嗅探的缺点
多线程面试题2_多线程_17
禁止指令重排序:
volatile通过内存屏障来禁止指令重排序
JMM内存屏障的策略
在每个volatile写操作的前面插入一个StoreStore屏障
在每个volatile写操作的后面插入一个StoreLoad屏障
在每个volatile读操作的后面插入一个LoadLoad屏障
在每个volatile读操作的后面插入一个LoadStore屏障

9.Condition和wait/notify的比较

多线程面试题2_多线程_18
多线程面试题2_线程池_19

10.JUC 下常用的同步工具类

CountDownLatch

它是一个同步辅助器,允许一个或多个线程一直等待,直到一组在其他线程执行的操作全部完成。
多线程面试题2_信号量_20

CyclicBarrier

一组线程会互相等待,直到所有线程都到达一个同步点。这个就非常有意思了,就像一群人被困到了一个栅栏前面,只有等最后一个人到达之后,他们才可以合力把栅栏(屏障)突破。

Semaphore

多线程面试题2_java_21
多线程面试题2_信号量_22

11.ThreadLocal

作用

多线程面试题2_信号量_23

应用场景

多线程面试题2_线程池_24

底层原理

多线程面试题2_信号量_25
多线程面试题2_数据_26
多线程面试题2_java_27

12.Executor提供了几种线程池

多线程面试题2_数据_28

13.线程池的参数

多线程面试题2_线程池_29
多线程面试题2_多线程_30

14.线程池拒绝策略

多线程面试题2_java_31
详解

15.执行execute()方法和submit()方法的区别是什么呢?

多线程面试题2_信号量_32

16.了解过线程池的工作原理吗?

多线程面试题2_线程池_33

17.Java中wait和sleep的区别

多线程面试题2_线程池_34

18.实现一下DCL

public class SingleInstance {
    private SingleInstance() {}
    private static volatile SingleInstance INSTANCE;
    public static SingleInstance getInstance() {
        if (INSTANCE == null) {
            synchronized (SingleInstance.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SingleInstance();
                }
            }
        }
        return INSTANCE;
    }
}

DDL单例为什么要加volatile
多线程面试题2_信号量_35
多线程面试题2_数据_36

19.实现一个阻塞队列(用Condition写生产者与消费者)

package com.milla.study.netbase.expert.concurrent.lock;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @Package: com.milla.study.netbase.expert.concurrent.lock
 * @Description: <阻塞队列>
 * @Author: MILLA
 * @CreateDate: 2020/6/1 18:14
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/6/1 18:14
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class ListBlockLockQueue {
    /**
     * 1.定义个队列
     * 2.采用可重入锁及信号量进行控制
     * 3.数据存储时,如果数据已经满了,则存储线程进行阻塞,唤醒获取数据线程
     * 4.数据获取时,如果当前数据已经消费完,则获取线程进行阻塞,获取存储数据线程
     * 5.数据的存储按照从0到队列的长度存入,如果存满则存储索引置0
     * 6.数据的获取按照从0到队列的长度获取,如果存满则存储索引置0
     */
 
    //定义队列容量
    private Integer size = 5;
    //采用集合定义一个队列
    private final Object[] data = new Object[size];
 
    //定义可重复锁
    ReentrantLock lock = new ReentrantLock();
    //获取为空信号量
    Condition take = lock.newCondition();
    //获取为满信号量
    Condition put = lock.newCondition();
    //获取数据索引值
    int takeIndex;
    //加入数据索引值
    int putIndex;
 
    //当前有多少个数据可取
    int count;
 
    /**
     * 拉取一个数据
     *
     * @return
     */
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            //没有数据可取-取数据线程阻塞
            while (count == 0) {
                take.await();//阻塞
            }
            //获取先存如数据
            Object o = data[takeIndex];
            //数据获取索引达到队列长度(说明当前数据取完了)-将获取索引置为0
            if (++takeIndex == data.length) {
                takeIndex = 0;
            }
            --count;
            System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
            put.signal();
            return o;
        } finally {
            lock.unlock();
        }
    }
 
    /**
     * 存入数据
     *
     * @param obj
     */
    public void put(Object obj) throws InterruptedException {
        //先上锁
        lock.lock();
        try {
            //如果已经满了-阻塞
            while (count == data.length) {
                //就不能继续添加
                put.await();
            }
            //添加元素
            data[putIndex] = obj;
            //添加数据索引达到队列长度-将索引值重置为0
            if (++putIndex == data.length) {
                putIndex = 0;
            }
            ++count;
            System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
            take.signal();
        } finally {
            //最后释放锁
            lock.unlock();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ListBlockLockQueue queue = new ListBlockLockQueue();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    queue.put("" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "一");
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    System.out.println(Thread.currentThread().getName() + ",获取数据: " + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "二");
        t2.start();
        t1.start();
        Thread.sleep(1000);
        queue.put("123456");
        Thread.sleep(1000);
        queue.put("00000");
        Thread.sleep(1000);
        queue.put("555555");
 
    }
}

详解

20.实现多个线程顺序打印abc?

public class JoinDemo{
    //三个线程顺序执行
    public static void main(String[] args) {
        Work t1=new Work(null,"A");
        Work t2=new Work(t1,"B");
        Work t3 =new Work(t2,"C");
        t1.start();
        t2.start();
        t3.start();
    }
     static class Work extends Thread{
        private Thread Thread;
        private String threadName;
         public Work(Thread Thread,String threadName) {
             this.Thread = Thread;
             this.threadName=threadName;
         }

         @Override
         public void run() {
             if (Thread != null) {
                 try {
                     Thread.join();

                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             System.out.println(threadName);
         }
     }

}

详解