JUC常用类

文章目录

  • JUC中常用的类
    • 1.原子类
    • 2.信号量 Semaphore
    • 3.ReentrantLock


JUC中常用的类

1.原子类

当我们在多线程环境下对变量进行更新时,会得到预期以外的值,典型的就是i++操作,通常会使用synchronized进行加锁保证原子性,但Java标准库中提供了一组原子类,原子类内部是使用CAS实现的,用法简单也更加高效。

  • AtomicBoolean:原子更新布尔值
  • AtomicInteger:原子更新整形
  • AtomicLong:原子更新长整形

java.util.concurrent.atomic包中提供了很多原子操作类,原子更新数组、原子更新引用类型等。

这里用AtomicInteger举个列子。两个线程同时对一个变量自增10w次,得到的结果也是预期的。

public class TestDemo6  {

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger integer = new AtomicInteger();

        Thread t1 = new Thread(()->{
            for (int i = 0; i < 100000; i++) {
                integer.getAndIncrement();
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 100000; i++) {
                integer.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(integer);
    }
}

2.信号量 Semaphore

信号量是一个比较抽象的词,举个列子:停止车的停车牌上写着当前停了多少量车,剩余车位。信号量也是如此用来限流的,用来控制访问特定资源的线程数量。

信号量本质上其实是一个计数器,表示可用资源的个数,申请资源一个资源可用资源就减一,释放一个资源可用资源数就加一。当计数为0的时候,继续申请资源机会阻塞,阻塞到其它线程释放了资源。

Java标准库中也提供了一个信号量的操作,Semaphore类。

public static void main(String[] args) throws InterruptedException {
    Semaphore semaphore = new Semaphore(100);
    semaphore.acquire();//申请资源
    semaphore.release();//释放资源
}

3.ReentrantLock

ReentrantLock也是可重入锁,使用lock()进行加锁操作,使用unlock()进行结解锁操作。和synchronized不同的是它是手动加锁解锁。期间可能会遇到异常问题导致不能解锁,所以一般把解锁操作放到finaly里面执行。

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    private static int count;
    private static ReentrantLock lock= new ReentrantLock();
    public static void add() {
       try{
           lock.lock();
           count++;
       } finally {
           lock.unlock();
       }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 100000; i++) {
                add();
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 100000; i++) {
                add();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

ReentranLock有一些特定的功能,synchronized做不到。

  1. tryLock()函数,也是加锁操作,试试能不能加锁,尝试成功就加锁,尝试失败了就放弃,并且可以指定加锁的等待超时时间

  2. ReentrantLock可以实现公平锁,默认是非公平的,构造的时候传入一个参数就是公平锁了

    ReentrantLock lock= new ReentrantLock(true);
    
  3. synchronized是搭配wait/notify实现等待通知机制的,唤醒操作是随机唤醒一个等待线程。而ReentrantLock搭配Condition类实现的,唤醒操作是可以指定唤醒哪个等待的线程的。


你可能感兴趣的:(多线程,java,多线程)