Java中多线程的乐观锁(CAS 机制)以及JUC关于线程的一些工具类(ReentrantLock,CountDownLatch,CyclicBarrier,Semaphore )的介绍

乐观锁

juc 中的大部分类是通过无锁并发实现的(没有用synchronized)

在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,
用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中
的 Collection 实现等

CAS 机制 (compare And swap 比较并交换)

synchronized 可以称之为悲观锁
cas 体现的是乐观锁
首先不是给共享资源加锁,而是做一个尝试
先拿到旧值,查看旧值是否跟共享区域的值相等
如果不相等,那么说明别的线程改动了共享区域的值,我的修改失败。
如果相等,那么就让我的修改成功。
如果修改失败了,会重新尝试。

我门可以分析下原子操作类的getAndAddInt()方法的源代码,体现的就是cas:

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
    	//获取共享区域的最新值
        var5 = this.getIntVolatile(var1, var2);
        //compareAndSwapInt比较并交换		var5:最新之,var5+var4:最新值+1
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
			//比较当前值与共享区域的值是否相等,相等返回true 取反是false,退出循环,
			//如果不相等就会返回false,取反是true ,不断循环。
			//实现无锁并发。乐观锁
    return var5;
}

ReentrantLock 重入锁

原理就是cas机制 ,可以当作synchronized ()来解决并发问题
lock() 加锁
unlock() 解锁

用ReentrantLock解决并发问题的例子

private static int i = 0;

public static void main(String[] args) throws InterruptedException {
    ReentrantLock lock = new ReentrantLock();
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 5000; j++) {
                try {

                    lock.lock();//加锁
                    i++;
                }finally {      //保证解锁一定被执行
                    lock.unlock();//解锁
                }
            }
        }
    });

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 5000; j++) {
                try {
                    lock.lock();
                    i--;
                }finally {
                    lock.unlock();
                }
            }
        }
    });
    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();

    System.out.println(i);

synchronized 性能上比较 ReentrantLock 在高并发下低,ReentrantLock的内存占用会高一些。

CountDownLatch

底层也是cas机制
使用场景: 当希望多个线程执行完毕后,再接着做下一步操作时。

CountDownLatch countDownLatch = new CountDownLatch(3);
Thread thread1=  new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程1开始"+new Date());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程1结束"+new Date());
        countDownLatch.countDown();             //倒计时-1
    }
});

Thread thread2=  new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程2开始"+new Date());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程2结束"+new Date());
        countDownLatch.countDown();
    }
});

Thread thread3=  new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程3开始"+new Date());
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程3结束"+new Date());
        countDownLatch.countDown();
    }
});
thread1.start();
thread2.start();
thread3.start();

//主线程
System.out.println("主线程等待");
countDownLatch.await();         //主线程等待,直到倒计时为0为止
System.out.println("执行主线程代码");

/*
主线程等待
线程2开始Fri Apr 05 11:11:57 CST 2019
线程1开始Fri Apr 05 11:11:57 CST 2019
线程3开始Fri Apr 05 11:11:57 CST 2019
线程2结束Fri Apr 05 11:11:58 CST 2019
线程3结束Fri Apr 05 11:11:59 CST 2019
线程1结束Fri Apr 05 11:11:59 CST 2019
执行主线程代码
 */

CyclicBarrier 循环栅栏

使用场景:当满足CyclicBarrier设置的线程个数时,继续执行,没有满足则等待。

//CyclicBarrier  可循环的 屏障(栅栏)
//当满足CyclicBarrier设置的线程个数时,继续执行,没有满足则等待
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);//表示有两个才会继续执行,没有就等待

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程1开始"+new Date());
        try {
            cyclicBarrier.await();      //当个数不足时,会等待 ,
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("线程1继续运行"+new Date());
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程2开始"+new Date());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            cyclicBarrier.await();      //2s后运行到这里,个数够了,继续运行。
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("线程2继续运行"+new Date());
    }
}).start();

/*
线程1开始Fri Apr 05 11:27:10 CST 2019
线程2开始Fri Apr 05 11:27:10 CST 2019
线程2继续运行Fri Apr 05 11:27:12 CST 2019
线程1继续运行Fri Apr 05 11:27:12 CST 2019
 */

与倒计时锁的区别:倒计时锁只能使用一次,倒计时结束这个对象就没用了。
而循环栅栏可以重复利用。

Semaphore 信号量

使用场景:限制了能同时运行的线程上线

Semaphore semaphore = new Semaphore(3);     //限制了能同时运行的线程上线 
for (int i = 0; i < 10; i++) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {


                try {
                    semaphore.acquire();    //获得信号量
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程" + Thread.currentThread().getName()+" "+new Date());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {                      //释放一定得执行
                semaphore.release();        //释放信号量
            }

        }
    }).start();
    
    /*
    我是线程Thread-0 Fri Apr 05 11:37:39 CST 2019
    我是线程Thread-2 Fri Apr 05 11:37:39 CST 2019
    我是线程Thread-1 Fri Apr 05 11:37:39 CST 2019
    我是线程Thread-6 Fri Apr 05 11:37:41 CST 2019
    我是线程Thread-3 Fri Apr 05 11:37:41 CST 2019
    我是线程Thread-5 Fri Apr 05 11:37:41 CST 2019
    我是线程Thread-8 Fri Apr 05 11:37:43 CST 2019
    我是线程Thread-7 Fri Apr 05 11:37:43 CST 2019
    我是线程Thread-4 Fri Apr 05 11:37:43 CST 2019
    我是线程Thread-9 Fri Apr 05 11:37:45 CST 2019
     */
}

你可能感兴趣的:(JavaSE)