java.util.concurrent.* java并发工具包中其他类

1. 线程(续)

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

CAS 机制 compare And swap 比较并交换

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

    java    10行

    int var5;
       // 修改失败,没关系,重新尝试 自旋
        do {
           // 获取共享区域的最新值
            var5 = this.getIntVolatile(var1, var2); // 10
                    // 比较并交换                                      最新值   最新值+1
        } while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;

1. 重入锁 ReentrantLock

.lock() 加锁
.unlock() 解锁
例子:

    java    34行

static int i = 0;

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

    Thread t1 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            try {
                rl.lock(); // 加锁
                i++;
            } finally {
                rl.unlock(); // 保证解锁一定被执行
            }
        }
    });

    Thread t2 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            try {
                rl.lock(); // 加锁
                i--;
            } finally {
                rl.unlock(); // 保证解锁一定被执行
            }
        }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);
}

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

2. 倒计时CountDownLatch

当希望多个线程都执行完毕后,再接着做下一步操作时,
例子:

    java    39行

public static void main(String[] args) throws InterruptedException {
    CountDownLatch cdl = new CountDownLatch(3);// 构造方法需要指定倒计时的数字
    new Thread(()->{
        System.out.println("线程1开始运行"+new Date());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程1准备完成"+new Date());
        cdl.countDown();
    }).start();
    new Thread(()->{
        System.out.println("线程2开始运行"+new Date());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程2准备完成"+new Date());
        cdl.countDown();
    }).start();
    new Thread(()->{
        System.out.println("线程3开始运行"+new Date());
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程3准备完成"+new Date());
        cdl.countDown();
    }).start();

    // 主线程等待,直到倒计时为0
    System.out.println("主线程等待");
    cdl.await();
    System.out.println("ready go....");
}

一个应用例子:模拟10个玩家加载进度

    java    28行

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(10);
    String[] all = new String[10];

    for (int j = 0; j < 10; j++) {
        int x = j;
        new Thread(()->{
            Random r = new Random();
            for (int i = 0; i <= 100; i++) {
                try {
                    Thread.sleep(r.nextInt(100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (all){
                    all[x]=(i+"%");
                    System.out.print("\r"+Arrays.toString(all));
                }
            }
            latch.countDown();

        }).start();
    }

    latch.await();
    System.out.println("\nend...");
}

3. 循环栅栏 CyclicBarrier

    java    33行

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

new Thread(()->{
    System.out.println("线程1开始.."+new Date());
    try {
        cb.await(); // 当个数不足时,等待
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程1继续向下运行..."+new Date());
}).start();

new Thread(()->{
    System.out.println("线程2开始.."+new Date());
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    try {
        cb.await(); // 2 秒后,线程个数够2,继续运行
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程2继续向下运行..."+new Date());
}).start();

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

4. 信号量 Semaphore

    java    20行

Semaphore s = new Semaphore(3); // 限制了能同时运行的线程上限
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            s.acquire(); // 获得此信号量
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            s.release(); // 释放信号量
        }

    }).start();

猜你喜欢

转载自blog.csdn.net/qq_38454165/article/details/81742551
今日推荐