JUC中的常见类

一.ReentrantLock

ReentrantLock是juc包中提供的一种锁,用来保证线程安全,其相对于synchronized存在一些不同点

  1. 上锁方式:ReentrantLock是手动进行上锁和解锁的,这就意味着相对于synchronized,reentrantlock更容易发生死锁现象,(当上锁之后,在解锁之前发生异常,这时候解锁无法进行,其他线程想要获取锁,这时候会发生死锁现象),为了避免死锁现象的产生,我们必须将reentrantLock上锁到解锁的代码放入trycatch finally代码块中,保证解锁的执行

ReentrantLock reentrantLock1 = new ReentrantLock();
        try {
            reentrantLock1.lock();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            reentrantLock1.unlock();
        }

需要注意的是ReentrantLock()中有一个tryLock()方法,它有一定的等待时间,如果在等待时间内获取不到锁,就放弃索取该锁,去执行其他任务

2.相对于synchronized,其支持公平锁:

ReentrantLock reentrantLock = new ReentrantLock(true);

3.阻塞和唤醒的方式:我们知道,synchronized使用的是Object类中的wait()、notify()和notifyall()的方法进行阻塞和唤醒,而ReentrantLock进行阻塞和解锁的方式如下:

 //创建对象
        ReentrantLock reentrantLock = new ReentrantLock(true);
        //需要手动进行上锁和解锁
        try {
            reentrantLock.lock();
            //创建condition对象
            Condition condition = reentrantLock.newCondition();
            condition.await();
            condition.signal();
            condition.signalAll();

reentrantLock对象利用newCondition()方法,创建阻塞对象(在底层创建一个条件对象),使用其await()、signal()、和signalAll()方法进行阻塞和唤醒,但是synchronized在底层维护一个阻塞队列和就绪队列,但是ReentrantLock中的每一个condition对象都会维护一个阻塞队列,不同线程会根据条件的不同进入不同的阻塞队列,最终维护一个就绪队列和多个阻塞队列

4.ReentrantLock支持读写锁:我们可以利用ReentrantReadWriteLock类创建读锁和写锁,其中读锁是共享锁,写锁是独占锁

 ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
            //获取读锁
            ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
            //获取写锁
            ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

二.原子类

原子类是利用CAS来来保证线程安全的实现方式,其性能要比加锁操作高很多

我们写以下代码来体会其作用:

扫描二维码关注公众号,回复: 14865820 查看本文章
public class TestAtomic {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomicInteger = new AtomicInteger();
         Thread t1 =new Thread(()->{
                    for (int i=0;i<50000  ;++i){
                        atomicInteger.getAndIncrement();
                    }
                 });
         t1.start();
          Thread t2 =new Thread(()->{
              for (int i = 0; i < 50000; i++) {
                  atomicInteger.getAndIncrement();
              }
                  });
          t2.start();
          t1.join();
          t2.join();
        System.out.println(atomicInteger);
    }
}

其内部机制:利用CAS+自旋保证原子性

我们没有使用synchronized进行上锁,却没有产生原子性问题,所以它的作用就像它的名字一样:保持原子性,我们去说明其中的几个方法:

三.JUC中的工具类

3.1 Semaphore(信号量)

semaphore用来记录可用资源的个数,在本质上说是一个计数器

可以把信号量想象成是停车场的展示牌: 当前有车位 100 个. 表示有 100 个可用资源.

当有车开进去的时候, 就相当于申请一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作)

当有车开出来的时候, 就相当于释放一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)

如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.

Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用.

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-08 16:06
 */
public class SemaphoreTest {
    public static void main(String[] args) {
        //创建信号量
        Semaphore semaphore = new Semaphore(5);
        //创建任务
     Runnable runnable=new Runnable() {
            @Override
            public void run() {
                //消耗资源
                try {
                    semaphore.acquire();
                    System.out.println( Thread.currentThread().getName()+"获取到资源了");
                    //执行任务
                    System.out.println(Thread.currentThread().getName()+"在执行任务");
                    //释放资源
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"释放资源了");
                    semaphore.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        };
     //通过for循环不断创建线程
        for (int i = 0; i <10 ; i++) {
             Thread thread=new Thread(runnable,i+"");
             thread.start();
        }


    }
}

3.2 CountDownLatch

同时等待 N 个任务执行结束.

好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author tongchen
 * @create 2023-02-08 16:21
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        //创建10个任务
        CountDownLatch countDownLatch = new CountDownLatch(10);
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                //准备就绪
                System.out.println(Thread.currentThread().getName()+"准备就绪");
                //开始运行
                System.out.println(Thread.currentThread().getName()+"开始运行");
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"运行结束");
                    countDownLatch.countDown();

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(runnable,"任务"+i);
            thread.start();
        }
        //等待任务全部执行结束
        countDownLatch.await();
        System.out.println("任务全部执行结束了......");
    }
}

3.3线程安全的集合类

当我们在多线程中使用集合类,同样会产生线程安全问题:

import java.util.ArrayList;
import java.util.Arrays;

/**
 * @author tongchen
 * @create 2023-02-08 16:34
 */
public class ArraylistWithMultithreading {
    public static void main(String[] args) {
        //创建集合
        ArrayList<Integer> arrayList = new ArrayList<>();
        //创建任务并加入多线程
        for (int i = 0; i < 10; i++) {
            int x=i;
            Thread  thread=new Thread(()->{
                    arrayList.add(x);
            });
            thread.start();
            System.out.println(arrayList);
        }
        System.out.println("-----------------------");
        System.out.println(arrayList);
    }
}

我们如何解决集合类中的线程安全问题呢?

  1. 手动加锁,使用synchronized包裹代码块或者使用reentrantlock手动加锁释放锁(一定要注意线程问题存在很多,我们在不同线程中进行读和写的操作都要记得加锁)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author tongchen
 * @create 2023-02-08 16:34
 */
public class ArraylistWithMultithreading {
   static Object loker=new Object();
    public static void main(String[] args) {
        //创建集合
        ArrayList<Integer> arrayList = new ArrayList<>();
        List<Integer> list = Collections.synchronizedList(arrayList);
        ReentrantLock reentrantLock = new ReentrantLock();
        CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);
        //创建任务并加入多线程
        for (int i = 0; i < 11; i++) {


                int x=i;
                Thread  thread=new Thread(()->{

                    reentrantLock.lock();
                        arrayList.add(x);
                    reentrantLock.unlock();


                });
            thread.start();
            reentrantLock.lock();
            System.out.println(arrayList);
            reentrantLock.unlock();

        }
        System.out.println("-----------------------");
    }
}
  1. 使用vector等线程安全的集合

  1. 使用工具类:使用Conllections类,将集合包裹


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author tongchen
 * @create 2023-02-08 16:34
 */
public class ArraylistWithMultithreading {
   static Object loker=new Object();
    public static void main(String[] args) {
        //创建集合
        ArrayList<Integer> arrayList = new ArrayList<>();
        List<Integer> list = Collections.synchronizedList(arrayList);
        CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);
        //创建任务并加入多线程
        for (int i = 0; i < 11; i++) {


                int x=i;
                Thread  thread=new Thread(()->{

                        list.add(x);


                });
                thread.start();


            System.out.println(list);
        }
        System.out.println("-----------------------");
        System.out.println(list);
    }
}
  1. 使用CopyOnWriteArraylist

如果是读Arraylist,不需要进行加锁,但是如果是对ArrayList进行写操作时,

此时使用 CopyOnWriteArrayList就是把这个ArrayList'给复制了一份,先修改

副本,修改之后引用再指向副本,保证修改的同时对于读操作是没有任何影响的,读的时候先读旧的版本,不会出现读到一个没修改完的中间状态,适用于多读少写的业务场景

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author tongchen
 * @create 2023-02-08 16:34
 */
public class ArraylistWithMultithreading {
   static Object loker=new Object();
    public static void main(String[] args) throws InterruptedException {
        //创建集合
        ArrayList<Integer> arrayList = new ArrayList<>();
        List<Integer> list = Collections.synchronizedList(arrayList);
        CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);
        //创建任务并加入多线程
        for (int i = 0; i < 5; i++) {


                int x=i;
                Thread  thread=new Thread(()->{

                        integers.add(x);


                });
                thread.start();
             


            System.out.println(integers);
        }
        System.out.println("-----------------------");
        System.out.println(integers);
    }
}

猜你喜欢

转载自blog.csdn.net/m0_65431718/article/details/128935574
今日推荐