JavaRush /Java 博客 /Random-ZH /第 26 级。有关该级别主题的面试问题的答案。第 2 部分:问题 6-9、11-12
zor07
第 31 级
Санкт-Петербург

第 26 级。有关该级别主题的面试问题的答案。第 2 部分:问题 6-9、11-12

已在 Random-ZH 群组中发布
第 26 级。有关该级别主题的面试问题的答案。 第 2 部分. 问题 6-9、11-12 - 1

6. 什么是坎卡伦齐?

并发是 Java 中的一个类库,其中包含针对跨多个线程工作而优化的特殊类。这些类被收集在一个包中java.util.concurrent。它们可以根据功能进行如下示意性划分: 第 26 级。有关该级别主题的面试问题的答案。 第 2 部分. 问题 6-9、11-12 - 2并发集合- 一组在多线程环境中比java.util包中的标准通用集合更有效地工作的集合。不是Collections.synchronizedList使用阻塞访问整个集合的基本包装器,而是使用数据段上的锁,或者使用无等待算法对数据的并行读取进行优化。 队列- 具有多线程支持的非阻塞和阻塞队列。非阻塞队列旨在提高速度并在不阻塞线程的情况下进行操作。当某些条件不满足时,您需要“减慢”“生产者”或“消费者”线程的速度,例如队列为空或溢出,或者没有空闲的“消费者”,则使用阻塞队列。 同步器是用于同步线程的辅助实用程序。它们是“并行”计算中的强大武器。 Executors - 包含用于创建线程池、调度异步任务和获取结果的优秀框架。 - 代表与基本同步机制 synchronized相比的替代且更灵活的线程同步机制。原子- 支持对基元和引用进行原子操作的类。 来源:waitnotifynotifyAll

7. 你知道 Kankarensi 的哪些课程?

这个问题的答案在这篇文章中得到了完美的阐述。我不认为在这里重印所有内容有什么意义,因此我将只描述那些我有幸简要熟悉的类。 ConcurrentHashMap<K, V> -与 和上的Hashtable块不同,数据以段的形式呈现,分为键的哈希值。因此,数据是按段访问的,而不是按单个对象访问的。另外,迭代器代表特定时间段的数据,不会抛出异常。 AtomicBoolean、AtomicInteger、AtomicLong、AtomicIntegerArray、AtomicLongArray - 如果在类中您需要同步访问一个简单类型变量怎么办?您可以将构造与, 一起使用,并且在使用原子操作,时使用。但是您可以通过使用新类做得更好。由于使用了 CAS,这些类的操作比通过. 另外,还有按给定数量进行原子加法以及递增/递减的方法。 synhronizedHashMapConcurrentModificationExceptionintsynchronizedset/getvolatileAtomic*synchronized/volatile

8. ConcurrentHashMap 类如何工作?

在其引入时,ConcurrentHashMapJava 开发人员需要以下哈希映射实现:
  • 线程安全
  • 访问整个表时没有锁
  • 执行读操作时最好没有表锁
主要实现思路ConcurrentHashMap如下:
  1. 地图元素

    与元素不同HashMapEntryinConcurrentHashMap被声明为volatile。这是一个重要的特性,也是由于JMM的变化所致。

    
    static final class HashEntry<K, V> {
        final K key;
        final int hash;
        volatile V value;
        final HashEntry<K, V> next;
    
        HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
            this .key = key;
            this .hash = hash;
            this .next = next;
            this .value = value;
         }
    
        @SuppressWarnings("unchecked")
        static final <K, V> HashEntry<K, V>[] newArray(int i) {
            return new HashEntry[i];
        }
    }
  2. 哈希函数

    ConcurrentHashMap还使用了改进的散列函数。

    HashMap让我提醒一下JDK 1.2中的情况:

    
    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    ConcurrentHashMap JDK 1.5 版本:

    
    private static int hash(int h) {
        h += (h << 15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h << 3);
        h ^= (h >>> 6);
        h += (h << 2) + (h << 14);
        return h ^ (h >>> 16);
    }

    为什么需要使哈希函数变得更复杂?哈希映射中的表的长度由 2 的幂决定。对于二进制表示在低位和高位上没有区别的哈希码,我们将会发生冲突。增加哈希函数的复杂度正好解决了这个问题,减少了映射中发生冲突的可能性。

  3. 该映射被分为N个不同的段(默认为16个,最大值可以是16位并且是2的幂)。每个段都是一个线程安全的映射元素表。增加段的数量将鼓励修改操作跨越多个段,从而降低运行时阻塞的可能性。

  4. 并发级别

    该参数影响存储卡的使用情况和卡中的段数。

    段的数量将被选择为大于 concurrencyLevel 的最接近的 2 的幂。降低 concurrencyLevel 会使线程在写入时更有可能阻塞映射段。高估该指标会导致内存使用效率低下。如果只有一个线程会修改map,其余线程都会读取,建议使用值1。

  5. 全部的

    那么,主要优点和实现特点ConcurrentHashMap

    • 地图有一个类似的hashmap交互界面
    • 读操作不需要锁并且并行执行
    • 写操作通常也可以并行执行而不会阻塞
    • 创建时注明需要的concurrencyLevel,通过读写统计来确定
    • 地图元素的值value声明为volatile
    来源: ConcurrentHashMap 的工作原理

9. 什么是Lock类?

为了控制对共享资源的访问,我们可以使用锁作为同步运算符的替代方案。锁定功能封装在java.util.concurrent.locks. 首先,线程尝试访问共享资源。如果它是空闲的,则在该线程上放置一个锁。一旦工作完成,共享资源上的锁就会被释放。如果资源未释放并且已在其上放置了锁,则线程将等待直到该锁被释放。锁类实现一个Lock定义以下方法的接口:
  • void lock():等待直到获取锁
  • boolean tryLock():尝试获取锁;如果获取锁,则返回true。如果未获取锁,则返回false。与该方法不同的是,lock()如果锁不可用,它不会等待获取锁
  • void unlock():移除锁
  • Condition newCondition():Condition返回与当前锁关联的对象
一般情况下锁定的组织非常简单:要获取锁,请调用该方法lock(),在完成共享资源的操作后,调用该方法unlock(),从而释放锁。该对象Condition允许您管理阻塞。通常,要使用锁,需要使用ReentrantLock包中的一个类。java.util.concurrent.locks.该类实现了 接口Lock。让我们以一个小程序为例来看看如何使用 Java Lock API: 那么,假设我们有一个类,它Resource有几个线程安全的方法和不需要线程安全的方法。

public class Resource {
 
    public void doSomething(){
        // пусть здесь происходит работа с базой данных 
    }
     
    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
现在让我们看一个实现接口Runnable并使用类方法的类Resource

public class SynchronizedLockExample implements Runnable{
 
    // экземпляр класса Resource для работы с методами
    private Resource resource;
     
    public SynchronizedLockExample(Resource r){
        this.resource = r;
    }
     
    @Override
    public void run() {
        synchronized (resource) {
            resource.doSomething();
        }
        resource.doLogging();
    }
}
现在让我们使用 Lock API 而不是synchronized.

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{
 
    private Resource resource;
    private Lock lock;
     
    public ConcurrencyLockExample(Resource r){
        this.resource = r;
        this.lock = new ReentrantLock();
    }
     
    @Override
    public void run() {
        try {
            // лочим на 10 секунд
            if(lock.tryLock(10, TimeUnit.SECONDS)){
            resource.doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //убираем лок
            lock.unlock();
        }
        // Для логгирования не требуется потокобезопасность
        resource.doLogging();
    }
 
}
从程序中可以看到,我们使用该方法tryLock()来确保线程只等待一定的时间。如果它没有获得对象的锁,它只会记录并退出。另一个重要的一点。必须使用块try-finally来确保即使方法doSomething()抛出异常也能释放锁。 资料来源:

11.什么是互斥体?

互斥体是用于同步线程/进程的特殊对象。它可以有两种状态——忙碌和空闲。简单来说,互斥体是一个布尔变量,它有两个值:busy (true) 和 free (false)。当一个线程想要一个对象的独占所有权时,它将其互斥体标记为忙碌,当它完成对对象的处理时,它将其互斥体标记为空闲。Java 中的每个对象都附加了一个互斥体。只有 Java 机器可以直接访问互斥体。它对程序员是隐藏的。

12.什么是显示器?

监视器是一种特殊的机制(一段代码) - 互斥锁上的附加组件,可确保其正确操作。毕竟,仅仅标记对象忙碌是不够的;我们还必须确保其他线程不会尝试使用忙碌对象。在Java中,监视器是使用关键字来实现的synchronized。当我们编写同步块时,Java编译器将其替换为三段代码:
  1. 在块的开头synchronized,添加了将互斥体标记为忙的代码。
  2. 在块的末尾,synchronized添加了一个代码,将互斥锁标记为空闲。
  3. 在该块之前,synchronized添加代码来检查互斥锁是否繁忙,然后线程必须等待它被释放。
第1部分
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION