JUC常用类介绍

JUC常用类介绍

Volatile

volatie只能保证线程之间的可见性,不能保证原子性。

/**
* 不能保证原子性的代码
*/
public class VolatileNotSync {
    volatile int count = 0;

    void m() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }

    public static void main(String[] args) {
        VolatileNotSync volatileNotSync = new VolatileNotSync();
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(volatileNotSync::m);
            list.add(thread);
        }
        list.forEach(Thread::start);
        for (Thread thread : list) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(volatileNotSync.count);
    }
}

Synchronized

既保证了可见性,也保证了原子性。jdk1.6 Synchronized做了优化,锁升级(无锁 -> 偏向锁 -> 乐观锁 -> 重量级锁)

锁升级.png

AtomicXXX

AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的。底层利用CAS实现

public class TestAtomic {
    AtomicInteger count = new AtomicInteger();

    void m() {
        for (int i = 0; i < 10000; i++) {
            // 多个方法连续调用,不能保证原子性
            if (count.get() < 1000)
                count.incrementAndGet();
        }
    }

    public static void main(String[] args) {
        TestAtomic testAtomic = new TestAtomic();
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(new Thread(testAtomic::m, "thread-" + i));
        }
        list.forEach(Thread::start);
        list.forEach((o) -> {
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(testAtomic.count);
    }
}

Reentrantlock

可以替代Synchronized,必须手动释放锁。而且可以指定公平锁。

public class TestReentrantLock {
    // true为公平锁,false为非公平锁,默认为false
    private static ReentrantLock lock = new ReentrantLock(true);

    void run() {
        for (int i = 0; i < 100; i++) {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "---");
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        TestReentrantLock t = new TestReentrantLock();
        Thread t1 = new Thread(t::run);
        Thread t2 = new Thread(t::run);
        t1.start();
        t2.start();
    }
}

CountDownLatch

java1.5被引入,使一个线程等待其他线程各自执行完毕后再执行。

通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

public class TestCountdownLatch {
    public static void main(String[] args) {
        usingCountDownLatch();
    }

    private static void usingCountDownLatch() {
        Thread[] threads = new Thread[100];
        CountDownLatch latch = new CountDownLatch(threads.length);
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                int result = 0;
                for (int j = 0; j < 10000; j++) result += j;
                latch.countDown();
            });
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end latch");
    }
}

CyclicBarrier

会让所有线程都等待完成后才会继续下一步行动。

比如,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。

参考:https://www.jianshu.com/p/333fd8faa56e

CountDownLatch和CyclicBarrier区别:

  1. countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
  2. CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
public class TestCyclicBarrier {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(20, () -> {
            System.out.println(Thread.currentThread().getName() + ":完成最后任务");
        });

        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "到达");
                    Thread.sleep(100);
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Phaser

JAVA 1.7引入了一个新的并发API:Phaser,一个可重用的同步barrier。用来解决控制多个线程分阶段共同完成任务的情景问题。

比如:5个学生一起参加考试,一共有三道题,要求所有学生到齐才能开始考试,全部同学都做完第一题,学生才能继续做第二题,全部学生做完了第二题,才能做第三题,所有学生都做完的第三题,考试才结束。分析这个题目:这是一个多线程(5个学生)分阶段问题(考试考试、第一题做完、第二题做完、第三题做完),所以很适合用Phaser解决这个问题。

参考:https://blog.csdn.net/u010739551/article/details/51083004

ReadWriterLock

JUC包主要是 ReentrantReadWriteLock。提供 readLock 和 writeLock 方法。

  1. ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
  2. ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
  3. 读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容,读写锁之间为互斥
  4. ReentrantReadWriteLock支持锁降级,不支持锁升级(同一个线程中,在没有释放读锁的情况下,就去申请写锁)。
public class TestReadWriteLock {
    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    static Lock readLock = lock.readLock();
    static Lock writeLock = lock.writeLock();

    void read() {
        readLock.lock();
        if (!lock.isWriteLocked()) {
            System.out.println(Thread.currentThread().getName() + "获取到读锁");
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "正在读...");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        readLock.unlock();
        System.out.println(Thread.currentThread().getName() + "释放读锁");
    }

    void write() {
        writeLock.lock();
        if (lock.isWriteLocked()) {
            System.out.println(Thread.currentThread().getName() + "获取到写锁");
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "正在写...");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        writeLock.unlock();
        System.out.println(Thread.currentThread().getName() + " 释放写锁");
    }

    public static void main(String[] args) {
        TestReadWriteLock t = new TestReadWriteLock();
        for (int i = 0; i < 3; i++) {
            new Thread(t::read).start();
        }
        for (int i = 0; i < 2; i++) {
            new Thread(t::write).start();
        }
        for (int i = 0; i < 3; i++) {
            new Thread(t::read).start();
        }
    }
}

Semaphore

Semaphore 是一个计数信号量,必须由获取它的线程释放。

常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。

Semaphore 只有3个操作:

  1. 初始化
  2. 增加
  3. 减少
public class TestSemaphore {
    public static void main(String[] args) {
        Semaphore s = new Semaphore(2, true);
        new Thread(() -> {
            try {
                s.acquire();
                System.out.println("t1.start");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                s.release();
            }
        }).start();
        new Thread(() -> {
            try {
                s.acquire();
                System.out.println("t2.start");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                s.release();
            }
        }).start();
    }
}

Exchanger

Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。

public class TestExchanger {
    static Exchanger<String> exchanger = new Exchanger<>();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            String t = "t1";
            try {
                t = exchanger.exchange(t);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-" + t);
        });
        Thread t2 = new Thread(() -> {
            String t = "t2";
            try {
                t = exchanger.exchange(t);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-" + t);
        });
        t1.start();
        t2.start();
    }
}

LockSupport

主要有park()和unpark()方法。park()阻塞当前线程,unpark()恢复当前线程。

与wait和notify的主要区别表现在 1.不需要获取某个对象的锁。2.没有前后顺序的区别

public class TestLockSupport {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                if (i == 5) {
                    LockSupport.park();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        try {
            // 保证先park后unpark
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LockSupport.unpark(t1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值