JavaRush /Blog Java /Random-VI /Cấp độ 26. Trả lời các câu hỏi phỏng vấn về chủ đề cấp độ...
zor07
Mức độ
Санкт-Петербург

Cấp độ 26. Trả lời các câu hỏi phỏng vấn về chủ đề cấp độ. Phần 2. Câu hỏi 6-9, 11-12

Xuất bản trong nhóm
Cấp độ 26. Trả lời các câu hỏi phỏng vấn về chủ đề cấp độ.  Phần 2. Câu 6-9, 11-12 - 1

6. Cancarenzi là gì?

Đồng thời là một thư viện lớp trong Java chứa các lớp đặc biệt được tối ưu hóa để làm việc trên nhiều luồng. Các lớp này được thu thập trong một gói java.util.concurrent. Chúng có thể được phân chia sơ đồ theo chức năng như sau: Cấp độ 26. Trả lời các câu hỏi phỏng vấn về chủ đề cấp độ.  Phần 2. Câu hỏi 6-9, 11-12 - 2Bộ sưu tập đồng thời - một tập hợp các bộ sưu tập hoạt động hiệu quả hơn trong môi trường đa luồng so với các bộ sưu tập phổ quát tiêu chuẩn từ java.utilgói. Thay vì trình bao bọc cơ bản Collections.synchronizedListcó chức năng chặn quyền truy cập vào toàn bộ bộ sưu tập, khóa trên các phân đoạn dữ liệu được sử dụng hoặc công việc được tối ưu hóa để đọc dữ liệu song song bằng thuật toán không cần chờ. Hàng đợi - hàng đợi không chặn và chặn với hỗ trợ đa luồng. Hàng đợi không chặn được thiết kế để đảm bảo tốc độ và hoạt động mà không chặn các luồng. Hàng đợi chặn được sử dụng khi bạn cần “làm chậm” các luồng “Nhà sản xuất” hoặc “Người tiêu dùng” nếu một số điều kiện không được đáp ứng, chẳng hạn như hàng đợi trống hoặc bị tràn hoặc không có “Người tiêu dùng” miễn phí. Bộ đồng bộ hóa là các tiện ích phụ trợ để đồng bộ hóa các luồng. Chúng là vũ khí mạnh mẽ trong điện toán “song song”. Người thực thi - chứa các khung tuyệt vời để tạo nhóm luồng, lên lịch các tác vụ không đồng bộ và thu được kết quả. Khóa - đại diện cho các cơ chế đồng bộ hóa luồng thay thế và linh hoạt hơn so với các synchronizedcơ chế waitcơ bản notify. Nguyên tử - các lớp có hỗ trợ cho các hoạt động nguyên tử trên nguyên hàm và tham chiếu. Nguồn:notifyAll

7. Bạn biết những lớp học nào ở Kankarensi?

Câu trả lời cho câu hỏi này được nêu một cách hoàn hảo trong bài viết này . Tôi thấy không có ích gì khi in lại tất cả ở đây, vì vậy tôi sẽ chỉ đưa ra mô tả về những lớp học mà tôi đã có vinh dự được làm quen trong thời gian ngắn. ConcurrentHashMap<K, V> - Không giống như Hashtablevà chặn synhronizedtrên HashMap, dữ liệu được trình bày dưới dạng phân đoạn, được chia thành các giá trị băm của khóa. Kết quả là dữ liệu được truy cập theo từng phân đoạn chứ không phải theo từng đối tượng. Ngoài ra, các trình vòng lặp biểu thị dữ liệu trong một khoảng thời gian cụ thể và không ném ConcurrentModificationException. AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray - Điều gì sẽ xảy ra nếu trong một lớp bạn cần đồng bộ hóa quyền truy cập vào một biến đơn giản thuộc loại int? Bạn có thể sử dụng các cấu trúc với synchronized, và khi sử dụng các phép toán nguyên tử set/get, volatile. Nhưng bạn thậm chí có thể làm tốt hơn bằng cách sử dụng các lớp mới Atomic*. Do sử dụng CAS nên các thao tác với các lớp này sẽ nhanh hơn nếu được đồng bộ hóa qua synchronized/volatile. Ngoài ra, còn có các phương pháp cộng nguyên tử với một lượng nhất định cũng như tăng/giảm.

8. Lớp ConcurrentHashMap hoạt động như thế nào?

Vào thời điểm được giới thiệu, ConcurrentHashMapcác nhà phát triển Java cần triển khai bản đồ băm sau:
  • An toàn chủ đề
  • Không có khóa trên toàn bộ bảng trong khi truy cập nó
  • Điều mong muốn là không có khóa bảng khi thực hiện thao tác đọc
Các ý tưởng triển khai chính ConcurrentHashMapnhư sau:
  1. Yếu tố bản đồ

    Không giống như các phần tử HashMap, Entryin ConcurrentHashMapđược khai báo là volatile. Đây là một tính năng quan trọng cũng do những thay đổi trong 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. Hàm băm

    ConcurrentHashMapmột hàm băm cải tiến cũng được sử dụng.

    Hãy để tôi nhắc bạn xem nó như thế nào trong HashMapJDK 1.2:

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

    Phiên bản từ 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);
    }

    Tại sao cần phải làm cho hàm băm phức tạp hơn? Các bảng trong bản đồ băm có độ dài được xác định bằng lũy ​​thừa hai. Đối với các mã băm có biểu diễn nhị phân không khác nhau ở vị trí cao và thấp, chúng ta sẽ xảy ra xung đột. Việc tăng độ phức tạp của hàm băm chỉ giải quyết được vấn đề này, giảm khả năng xảy ra xung đột trên bản đồ.

  3. Phân đoạn

    Bản đồ được chia thành N phân đoạn khác nhau (16 theo mặc định, giá trị tối đa có thể là 16 bit và là lũy thừa của hai). Mỗi phân đoạn là một bảng các phần tử bản đồ an toàn theo luồng. Việc tăng số lượng phân đoạn sẽ khuyến khích các hoạt động sửa đổi trải rộng trên nhiều phân đoạn, giảm khả năng bị chặn khi chạy.

  4. Cấp độ đồng thời

    Thông số này ảnh hưởng đến việc sử dụng thẻ nhớ và số lượng phân đoạn trong thẻ.

    Số lượng phân đoạn sẽ được chọn là lũy thừa gần nhất của hai phân đoạn lớn hơn mức đồng thời. Việc giảm concurrencyLevel khiến nhiều khả năng các luồng sẽ chặn các phân đoạn bản đồ khi viết. Đánh giá quá cao chỉ số dẫn đến việc sử dụng bộ nhớ không hiệu quả. Nếu chỉ có một luồng sửa đổi bản đồ và phần còn lại sẽ đọc thì nên sử dụng giá trị 1.

  5. Tổng cộng

    Vì vậy, những ưu điểm chính và tính năng thực hiện ConcurrentHashMap:

    • hashmapBản đồ có giao diện tương tác tương tự như
    • Hoạt động đọc không yêu cầu khóa và được thực hiện song song
    • Hoạt động ghi thường có thể được thực hiện song song mà không bị chặn
    • Khi tạo, yêu cầu được chỉ định concurrencyLevel, được xác định bằng cách đọc và ghi số liệu thống kê
    • Các phần tử bản đồ có giá trị valueđược khai báo làvolatile
    Nguồn: Cách thức hoạt động của ConcurrentHashMap

9. Lớp Khóa là gì?

Để kiểm soát quyền truy cập vào tài nguyên được chia sẻ, chúng ta có thể sử dụng khóa thay thế cho toán tử được đồng bộ hóa. Chức năng khóa được đóng gói trong java.util.concurrent.locks. Đầu tiên, luồng cố gắng truy cập tài nguyên được chia sẻ. Nếu nó rảnh, thì một khóa sẽ được đặt trên sợi chỉ. Sau khi công việc hoàn thành, khóa tài nguyên được chia sẻ sẽ được giải phóng. Nếu tài nguyên không còn trống và một khóa đã được đặt trên đó thì luồng sẽ đợi cho đến khi khóa này được giải phóng. Các lớp khóa triển khai một giao diện Lockxác định các phương thức sau:
  • void lock():đợi cho đến khi có được khóa
  • boolean tryLock():cố gắng lấy được khóa; nếu lấy được khóa, nó sẽ trả về true . Nếu không lấy được khóa, nó sẽ trả về false . Không giống như phương pháp này, lock()nó không chờ để lấy khóa nếu không có sẵn
  • void unlock():loại bỏ khóa
  • Condition newCondition():trả về đối tượng Conditionđược liên kết với khóa hiện tại
Việc tổ chức khóa trong trường hợp chung khá đơn giản: để lấy khóa, phương thức được gọi lock()và sau khi làm việc xong với tài nguyên dùng chung, phương thức được gọi unlock(), phương thức này sẽ giải phóng khóa. Đối tượng Conditioncho phép bạn quản lý việc chặn. Theo quy định, để làm việc với các khóa, một lớp ReentrantLocktrong gói sẽ được sử dụng. java.util.concurrent.locks.Lớp này triển khai giao diện Lock. Hãy xem việc sử dụng Java Lock API bằng một chương trình nhỏ làm ví dụ: Vì vậy, giả sử chúng ta có một lớp Resourcevới một vài phương thức an toàn luồng và các phương thức không yêu cầu an toàn luồng.
public class Resource {

    public void doSomething(){
        // пусть здесь происходит работа с базой данных
    }

    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
Bây giờ chúng ta hãy lấy một lớp thực hiện giao diện Runnablevà sử dụng các phương thức lớp 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();
    }
}
Bây giờ hãy viết lại chương trình trên bằng API Lock thay vì 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();
    }

}
Như bạn có thể thấy trong chương trình, chúng tôi sử dụng phương thức này tryLock()để đảm bảo rằng luồng chỉ đợi trong một khoảng thời gian nhất định. Nếu nó không có được khóa trên đối tượng, nó chỉ cần ghi nhật ký và thoát. Một điểm quan trọng khác. Bạn phải sử dụng một khối try-finallyđể đảm bảo rằng khóa sẽ được giải phóng ngay cả khi phương thức doSomething()đưa ra một ngoại lệ. Nguồn:

11. Mutex là gì?

Mutex là một đối tượng đặc biệt để đồng bộ hóa các luồng/quy trình. Nó có thể có hai trạng thái - bận và rảnh. Để đơn giản hóa, mutex là một biến boolean nhận hai giá trị: bận (đúng) và rảnh (sai). Khi một luồng muốn có quyền sở hữu độc quyền đối với một đối tượng, nó sẽ đánh dấu mutex của nó là bận và khi làm việc xong với đối tượng đó, nó sẽ đánh dấu mutex của nó là rảnh. Một mutex được gắn vào mọi đối tượng trong Java. Chỉ máy Java mới có quyền truy cập trực tiếp vào mutex. Nó được ẩn khỏi lập trình viên.

12. Màn hình là gì?

Màn hình là một cơ chế đặc biệt (một đoạn mã) - một tiện ích bổ sung trên mutex, đảm bảo hoạt động chính xác với nó. Cuối cùng, việc đánh dấu rằng đối tượng đang bận là chưa đủ; chúng ta cũng phải đảm bảo rằng các luồng khác không cố gắng sử dụng đối tượng bận. Trong Java, màn hình được triển khai bằng từ khóa synchronized. Khi chúng ta viết một khối được đồng bộ hóa, trình biên dịch Java sẽ thay thế nó bằng ba đoạn mã:
  1. Ở đầu khối synchronized, mã được thêm vào để đánh dấu mutex là bận.
  2. Ở cuối khối, synchronizedmột mã được thêm vào để đánh dấu mutex là miễn phí.
  3. Trước khối, synchronizedmã được thêm vào để kiểm tra xem mutex có bận hay không, sau đó luồng phải đợi nó được giải phóng.
Phần 1
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION