面试题总结 - 多线程 并发

1.1 线程间通信的方式有哪些

  1. 传统的线程通信
    使用synchronized 修饰的同步方法或者同步代码块,使用Object类下的wait() notify() notifyAll() 方法控制线程通信
    wait() 导致该线程阻塞等待其他线程使用notify() 方法唤醒
    notify()唤醒在此同步监视器的某一个线程
    notifyAll() 唤醒此同步监视器的全部的线程

  2. 使用Condition
    使用Lock 当作同步的对象,由于没有隐式的监视器,使用Condition类的await() signal() signalAll()方法实现通信

  3. 使用组赛队列
    BlockingQueue 实现消费者生产者
    put() 尝试往队列里面放入元素,满了就会阻塞
    take() 尝试拿出元素 空就阻塞

BlockingQueue实现生产者 消费者

public class BlockingQueueTest3 {

    class Producer extends Thread{

        public BlockingQueue<String> bq;
        public Producer(BlockingQueue<String> queue){
            this.bq = queue;
        }

        @Override
        public void run(){
            String product_name = Thread.currentThread().getName();
            System.out.println("我已经生产了"+Thread.currentThread().getName());
            try {
                bq.put(product_name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class Consumer extends Thread{
        BlockingQueue<String> bq;

        public Consumer(BlockingQueue<String> bq){
            this.bq = bq;
        }
        @Override
        public void run(){
            try {
                String product_name = bq.take();
                System.out.println("消费了:"+product_name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        BlockingQueue<String> bq = new LinkedBlockingDeque<>(1);
        BlockingQueueTest3 bb = new BlockingQueueTest3();
        for (int i = 0; i < 5; i++) {
            bb.new Producer(bq).start();
            bb.new Consumer(bq).start();
        }
    }
}

1.2 HashMap,ConcurrentHashMap

1.3 synchronized加到普通方法和静态方法的区别

static 获得的是类的锁,非静态获得到的是实例的锁

不会产生互斥

1.4 一个类的两个方法都加了synchronized,是一个锁还是两个锁;

一个,同一个对象锁只能被一个线程占用

1.5 volatile

volatile 的作用:

  1. 保证被volatile修饰的共享变量对所有线程总是可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知。
  2. 加了CPU的内存屏障指令,禁止指令重排序优化

volatile 如何做到可见性:
在这里插入图片描述
我们知道处理器速度与主存速度是不一致的,每个cpu自己维护一个处理缓存,当两个线程修改同一份数据的时候就会出现数据不一致的情况,加上volatile 关键字后,相当于加了一个内存屏障指令,然后:

  • 当线程修改这个变量的时候会立即刷新到主内存
  • 当读取该变量的时候会把该线程对应的本地内存设置无效,然后去主内存读取。

1.6 synchronized

可以修饰成员方法(实例) 静态方法(类对象) 代码块(括号后面的对象)
先用javac Test.class 编译出.class文件
然后用javap -c Test.class 查看字节码

  • 修饰对象是基于 进出 monitor对象实现
  • 修饰方法是基于ACC_SYNCHRONIZED修饰,表示方法加锁了。它会在常量池中增加这个一个标识符,获取它的monitor,所以本质上是一样的。
    实现锁的状态管理是在对象头中的mark work
    锁的四种状态:
  1. 偏向锁
    一个线程每次进出方法都要解锁加锁,这样会造成资源浪费,所以引出偏向锁,在线程第一次获得锁的时候就在对象头中的锁标志位标注位偏向锁
  2. 轻量级锁
    当线程A获得偏向锁后,线程B进入竞争状态,需要获得线程A持有的锁,那么线程A撤销偏向锁,进入无锁状态。线程A和线程B交替进入临界区,偏向锁无法满足,膨胀到轻量级锁,锁标志位设为00。
  3. 重量级锁
    当多线程交替进入临界区,轻量级锁hold得住。但如果多个线程同时进入临界区,hold不住了,膨胀到重量级锁

1.7 volatile与synchronized区别和使用场景

  • volatile 是一种轻量级锁,synchronized是重量级锁(悲观锁)
  • 对于多线程 volatile 不是互斥的关系,synchronized 是互斥的,
  • volatile不能保证变量状态的“原子性操作”

1.8

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值