线程锁详解(6种常用线程锁)

线程锁详解(6种常用线程锁)-mikechen

线程锁经常在Java面试被问到,比如:常见的线程锁有哪些?下面我重点详解6种常用的线程锁@mikechen

1.Synchronized关键字

Synchronized关键字是Java内置的锁机制,它可以将某个代码块或方法声明为同步的。

如下所示:

public class SynchronizedExample {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count is: " + myThread.getCount()); // 输出2000
    }
}

class MyThread implements Runnable {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            increment();
        }
    }

    public int getCount() {
        return count;
    }
}

当一个线程进入同步代码块或方法时,它将获取锁并执行代码,其他线程必须等待锁被释放后才能进入同步代码块或方法。

 

2.ReentrantLock类

ReentrantLock是Java提供的可重入锁,它允许一个线程多次获得同一个锁。

如下所示:

class MyThread implements Runnable {
    private int count = 0;
    private ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            increment();
        }
    }

    public int getCount() {
        return count;
    }
}

public class ReentrantLockExample {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count is: " + myThread.getCount()); // 输出2000
    }
}

ReentrantLock可以显式地获取和释放锁,而Synchronized关键字则由Java虚拟机隐式地获取和释放锁。

使用ReentrantLock时需要注意正确获取和释放锁,否则可能会导致死锁等问题。

 

3.ReadWriteLock类

ReadWriteLock是Java提供的读写锁机制,它允许多个线程同时读取某个共享资源,但只允许一个线程写入共享资源。

如下所示:

class MyThread implements Runnable {
    private int count = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void increment() {
        lock.writeLock().lock();
        try {
            count++;
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            increment();
        }
    }

    public int getCount() {
        lock.readLock().lock();
        try {
            return count;
        } finally {
            lock.readLock().unlock();
        }
    }
}

public class ReadWriteLockExample {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count is: " + myThread.getCount()); // 输出2000
    }
}

读写锁通过提高并发性能来优化读写操作。

 

4.Semaphore类

Semaphore是Java提供的信号量机制,它可以控制对共享资源的访问线程数量。

如下所示:

public static void main(String[] args) {
    // 表示有2个许可.
    Semaphore sem = new Semaphore(2);
    for (int i = 0; i < 3; i++) {
        new Thread(() -> {
            try {
                // 默认使用一个许可.
                sem.acquire();
                System.out.println(Thread.currentThread() + " I get it.");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread() + " I release it.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                sem.release();
            }
        }).start();
    }
}

Semaphore维护一个计数器,当有线程请求访问共享资源时,计数器减一,当计数器为0时,其他线程将被阻塞,直到一个线程释放资源并将计数器加一。

 

5.CountDownLatch类

CountDownLatch是Java提供的倒计时锁机制,它允许一个或多个线程等待一个或多个其他线程执行完毕后再继续执行。

每当一个线程完成了自己的任务后,计数器的值就会减1,当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

如下图所示:

线程锁详解(6种常用线程锁)-mikechen

示例:

public class CountDownLatchDemo{

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i + 1;
            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(no + "号运动员完成了比赛。");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                }
            };
            service.submit(runnable);
        }
        System.out.println("等待5个运动员都跑完.....");
        latch.await();
        System.out.println("所有人都跑完了,比赛结束。");
    }
}

在比赛跑步时有 5 个运动员参赛,终点有一个裁判员,什么时候比赛结束呢?那就是当所有人都跑到终点之后,宣布比赛结束。

 

6.CyclicBarrier类

CyclicBarrier是Java提供的屏障机制,它允许多个线程在某个屏障处等待,当所有线程都到达屏障时,才继续执行。

如下图所示:

线程锁详解(6种常用线程锁)-mikechen

CyclicBarrier维护一个计数器和一个屏障操作,每个线程调用await()方法时将计数器减一,并等待其他线程到达屏障,当计数器为0时,所有线程被释放并继续执行。

以上就是6种常见的线程锁详解,更多多线程内容,请查看:Java多线程编程详解(看这篇就足够了)

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法