线程相关—锁

来源:互联网 发布:天互数据备案 编辑:程序博客网 时间:2024/06/16 13:55

1.闭锁CountDownLatch

闭锁:想实现它管理的线程都执行完后,在执行其它线程。
在调用构造方法创建CountDownLatch对象时需要指定管理线程的个数(计数器的值)。
await():会产生阻塞,直到计数器减为0的时候才会释放。
countDown():每调用一次,会将计数器–。

例子:锅和菜买回来之后,才可以输出开始做饭

//两个线程类class BuyGuo implements Runnable{    private CountDownLatch cdl ;    public BuyGuo(CountDownLatch cdl){        this.cdl = cdl;    }    public void run(){        System.out.println("锅买回来了...");        cdl.countDown();    }}class BuyCai implements Runnable{    private CountDownLatch cdl;    public BuyCai(CountDownLatch cdl){        this.cdl = cdl;    }    public void run(){        System.out.println("菜买回来了...");        cdl.countDown();    }}//主方法public static void main(String[] args) {        //创建闭锁,管理2个线程        CountDownLatch cdl = new CountDownLatch(2);        //将闭锁传入。        new Thread(new BuyGuo(cdl)).start();        new Thread(new BuyCai(cdl)).start();        try {            //阻塞,直到上述2个线程执行完毕再继续执行。            cdl.await();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("开始做饭...");    }

2.栅栏 CyclicBarrier

也需要在创建时确定管理线程个数,await()方法是加法了,产生阻塞并且计数器加一,当加到管理线程的个数时,释放阻塞。

public class DemoCyclicBarrier {    public static void main(String[] args) {        CyclicBarrier cb = new CyclicBarrier(2);        new Thread(new Horse1(cb)).start();        new Thread(new Horse2(cb)).start();    }}class Horse1 implements Runnable{    private CyclicBarrier cb;    public Horse1(CyclicBarrier cb){        this.cb = cb;    }    public void run(){        System.out.println("第一匹马来到起跑线,做好了准备..");        try {        //阻塞,知道计数器为2时才释放。            cb.await();        } catch (InterruptedException e) {            e.printStackTrace();        } catch (BrokenBarrierException e) {            e.printStackTrace();        }        System.out.println("第一匹马开始比赛...跑起");    }}class Horse2 implements Runnable{    private CyclicBarrier cb;    public Horse2(CyclicBarrier cb){        this.cb = cb;    }    public void run(){        System.out.println("第二匹马正在拉肚子ing....");        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("第二匹马来到了起跑线,准备好...");        try {        //阻塞,知道计数器为2时才释放。            cb.await();        } catch (InterruptedException e) {            e.printStackTrace();        } catch (BrokenBarrierException e) {            e.printStackTrace();        }        System.out.println("第二匹马开始比赛...跑起");    }}//两匹马同时起跑~~

CountDownLatch和CyclicBarrier的主要联系和区别如下:(这段转自千山独行大大的博客~)
1.闭锁CountDownLatch做减计数,而栅栏CyclicBarrier则是加计数。
2.CountDownLatch是一次性的,CyclicBarrier可以重用。
3.CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成。
4.鉴于上面的描述,CyclicBarrier在一些场景中可以替代CountDownLatch实现类似的功能。

3.交换机 Exchanger

public class DemoExchanger {    public static void main(String[] args) {    //创建交换机。        Exchanger<String> exc = new Exchanger<String>();        new Thread(new Spy1(exc)).start();        new Thread(new Spy2(exc)).start();    }}class Spy1 implements Runnable{    private Exchanger<String> exc;    public Spy1(Exchanger<String> exc){        this.exc = exc;    }    public void run(){        //返回的是线程2传给线程1的内容        try {        //该方法给线程传信息,并接收对方传的信息。            String msg = exc.exchange("天王盖地虎");            System.out.println("间谍2传给间谍1的信息:"+msg);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}class Spy2 implements Runnable{    private Exchanger<String> exc;    public Spy2(Exchanger<String> exc){        this.exc = exc;    }    public void run(){        //返回的是线程2传给线程1的内容        try {        //该方法给线程传信息,并接收对方传的信息。            String msg = exc.exchange("宝塔镇河妖");            System.out.println("间谍1传给间谍2的信息:"+msg);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

4.锁—lock

之前线程安全问题需要解决,都会用synchronized同步代码块,这个方法我们还需要指定锁的钥匙,不够灵活。

synchronized:
假设切换线程时,线程1运行一次,然后等待,唤醒线程2,线程2运行。这期前假设运行时间1s,唤醒时间2秒。那么运行2次的时间一共是4秒。
lock:lock在唤醒期间,如果线程2没醒会多次运行,线程1运行1秒后,线程2处再唤醒中,那么线程1会在唤醒的2秒中继续运行2次,等线程2唤醒后,线程2运行。那么4秒的时间一共运行了4次。

lock代码具体如下:

public class TestDemo3 {    public static String name="李雷";    public static String gender = "男";    public static void main(String[] args) {        Lock lock = new ReentrantLock();        new Thread(new ReadRunner3(lock)).start();        new Thread(new WriteRunner3(lock)).start();    }}class ReadRunner3 implements Runnable{    private Lock lock;    public ReadRunner3(Lock lock){        this.lock = lock;    }    public void run(){        while(true){            //添加锁            lock.lock();                System.out.println(TestDemo3.name+","+TestDemo3.gender);            //释放锁,如果涉及到异常处理的代码,该行代码一定要放在finally中            lock.unlock();        }    }}class WriteRunner3 implements Runnable{    private Lock lock;    public WriteRunner3(Lock lock){        this.lock= lock;    }    public void run(){        while(true){            lock.lock();                if("李雷".equals(TestDemo3.name)){                    TestDemo3.name = "韩梅梅";                    TestDemo3.gender ="女";                }else{                    TestDemo3.name="李雷";                    TestDemo3.gender="男";                }            lock.unlock();        }    }}

读写锁ReadWriteLock
读写锁分为读锁和写锁,与Lock的区别就是,读锁和读锁之间可以共存,如果是对一条数据的多个请求的读操作,不会进行锁定,但是读锁和写锁,写锁和写锁会被锁定。

代码升级:

public class TestDemo4 {    public static String name="李雷";    public static String gender = "男";    public static void main(String[] args) {        ReadWriteLock lock = new ReentrantReadWriteLock ();        new Thread(new ReadRunner4(lock)).start();        new Thread(new WriteRunner4(lock)).start();    }}class ReadRunner4 implements Runnable{    private ReadWriteLock lock;    public ReadRunner4(ReadWriteLock lock){        this.lock = lock;    }    public void run(){        while(true){            //添加锁            lock.readLock().lock();                System.out.println(TestDemo4.name+","+TestDemo4.gender);            //释放锁,如果涉及到异常处理的代码,该行代码一定要放在finally中            lock.readLock().unlock();        }    }}class WriteRunner4 implements Runnable{    private ReadWriteLock lock;    public WriteRunner4(ReadWriteLock lock){        this.lock= lock;    }    public void run(){        while(true){            lock.writeLock().lock();                if("李雷".equals(TestDemo4.name)){                    TestDemo4.name = "韩梅梅";                    TestDemo4.gender ="女";                }else{                    TestDemo4.name="李雷";                    TestDemo4.gender="男";                }            lock.writeLock().unlock();        }    }}

5.原子性AtomicInteger

如果设置一个静态成员变量,2个线程分别从1加到100000,那么结束后输出最后的结果,会是多少呢?
会是200000嘛?不会,因为线程安全没有任何措施,导致会有重复的相加,即两个线程同时加完后结果只加了1.而不是加2.
添加synchronized同步代码块或锁可以解决该问题,我们还有别的解决方案,原子性AtomicInteger

public class DemoAtomic {    //新建原子性变量。    public static AtomicInteger num =            new AtomicInteger(0);    public static void main(String[] args) {    //闭锁,全部完成后再输出结果        CountDownLatch cdl = new CountDownLatch(2);        new Thread(new AddRunner(cdl)).start();        new Thread(new AddRunner(cdl)).start();        try {            cdl.await();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(num);    }}class AddRunner implements Runnable{    private CountDownLatch cdl;    public AddRunner(CountDownLatch cdl){        this.cdl = cdl;    }    public void run(){        for (int i = 0; i < 100000; i++) {            //每次加一 相当于num++            DemoAtomic.num.getAndAdd(1);        }        cdl.countDown();    }}

其源码的逻辑是每次加完后会进行安全检查,如果发现重复相加则再加一次,直至没有重复相加。

原创粉丝点击