多线程与高并发day05

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

个人学习笔记,仅供参考,欢迎指正!
原子性相对复杂,且各种面试题层出不穷,需要认真学习!


一、补缺

1.补缺

  1. unsafe类。
    在jdk1.8及之前是没有getUnsafe()方法,只能通过反射调用。jdk11的源码中提供了单例模式的调用。
    在这里插入图片描述
    在这里插入图片描述

Unsafe类可以直接操纵Java虚拟机中的内存
在这里插入图片描述

所有的Atomic开头的类,内部都是通过Unsafe类里面的CompareAndSwap、CompareAndSet这一类的原子性操作、CAS操作来实现的。

在这里插入图片描述

allocateMemory方法可以直接分配内存,返回一个long类型的指针。

在这里插入图片描述

  1. LongAddr 的使用.
    下面是对AtomicLong 、LongAddr、Synchronized的三种方式递增的一个粗糙的测试。在真实环境中,需要经过真实的测试才能做出判断。
public class Day05 {
    private static long count1 = 0L;
    private static AtomicLong count2 = new AtomicLong(0L);
    private static LongAdder count3 = new LongAdder();


    public static void main(String[] args) throws InterruptedException {
        Day05 day05 = new Day05();
        Thread[] threads = new Thread[1000];

        for (int i = 0; i < threads.length; i++) {
            threads[i]=new Thread(()->{
                for (int j = 0; j < 100000; j++) count2.incrementAndGet();
            });
        }
        long start = System.currentTimeMillis();
        for (Thread thread : threads) thread.start();
        for (Thread thread : threads) thread.join();
        long end = System.currentTimeMillis();
        System.out.println("Atomic: "+count2.get()+"time "+(end-start));

        Object o = new Object();
        for (int i = 0; i < threads.length; i++) {
            threads[i]=new Thread(()->{
                for (int j = 0; j < 100000; j++) {
                    synchronized (o){
                        count1++;
                    }
                }
            });
        }
        start = System.currentTimeMillis();
        for (Thread thread : threads) thread.start();
        for (Thread thread : threads) thread.join();
        end = System.currentTimeMillis();
        System.out.println("Synchronized: "+count1+"time "+(end-start));
        
        for (int i = 0; i < threads.length; i++) {
            threads[i]=new Thread(()->{
                for (int j = 0; j < 100000; j++) count3.increment();
            });
        }
        start = System.currentTimeMillis();
        for (Thread thread : threads) thread.start();
        for (Thread thread : threads) thread.join();
        end = System.currentTimeMillis();
        System.out.println("LongAdder: "+count3.longValue()+"time "+(end-start));
    }
}

LongAddr快的原因:LongAdder的内部做了一个分段锁,类似于分段锁的概念。在它内部的时候,会把一个值放到一个数组里,比如说数组长度是4,最开始是0,1000个线程,250个线程锁在第一个数组元素里,以此类推,每一个都往上递增算出来结果在加到一起。

在这里插入图片描述

二、ReentrantLock

1.定义

ReentrantLock是Lock接口的实现类,可以手动的对某一段进行加锁。ReentrantLock可重入锁,具有可重入性,并且支持可中断锁。其内部对锁的控制有两种实现,一种为公平锁,另一种为非公平锁。ReentrantLock的实现原理为volatile+CAS。ReentrantLock具有可重入性,可重入性是指当一个线程拥有一个方法的锁以后,是否还可以进入该方法,一般这种情况出现在递归中。ReentrantLock的可重入性是基于Thread.currentThread()实现的,是线程粒度的,也就是说当前线程获得一个锁以后,当前线程的所有方法都可以获得这个锁。
需要注意的是:使用ReentrantLock必须手动释放锁。

2.用法

ReentrantLock是可以代替synchronized使用的,他还可以进行tryLock,lockInterupptibly,公平非公平切换等操作。
代码如下(示例):

public class Day05 {
    Lock lock = new ReentrantLock();
    public void m1() {
        try {
            lock.lock();
            for (int i = 0; i < 3; i++) {
                Thread.sleep(1000);
                System.out.println(i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
     * 也可以根据tryLock的返回值来判定是否锁定
     * 也可以指定tryLock的时间,犹豫tryLock(time)抛出异常,所以要注意unlock的使用时机
     */
    public void m2() {
        boolean locked = false;
        try {
            locked = lock.tryLock(5, TimeUnit.SECONDS);
            System.out.println("...m2"+locked);

        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            if (locked) lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Day05 day05 = new Day05();
        new Thread(day05::m1).start();
        Thread.sleep(1000);
        new Thread(day05::m2).start();
    }
}

这段代码目前有误:待修改…

public class Day05 {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            try {
                lock.lock();
                System.out.println("t1 start");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                System.out.println("t1 end");
            } catch (InterruptedException e) {
                System.out.println("interrupted!");
            } finally {
                lock.unlock();
            }
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            try {
                lock.lockInterruptibly();
                System.out.println("t2 start");
                TimeUnit.SECONDS.sleep(5);
                System.out.println("t2 end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        t2.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.interrupt();//打断线程2的等待
    }
}

公平锁:也不是说完全公平,会先查看队列中是否有等待的线程,如果有则进入排队,没有则直接获取锁。非公平锁,不会去管队列中是否有任务,会直接去抢锁。

public class Day05 extends Thread {
    private static ReentrantLock lock = new ReentrantLock(true);//参数为true表示为公平锁,请对比输出结果
    public void run(){
        for (int i = 0; i < 100; i++) {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"获得锁");
            }finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Day05 day05 = new Day05();
        Thread th1 = new Thread(day05);
        Thread th2 = new Thread(day05);
        th1.start();
        th2.start();
    }
}

在这里插入图片描述

三、阻塞

1.CountDownLatch

示例代码如下:
CountDownLatch使用比join更加灵活,可以在单个线程里面不断countDown。它像一个门栓一样,把所有的枷锁减下去,才能继续前进。

    public static void main(String[] args) {
        usingJoin();
        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");
    }

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

2.CyclicBarriar

每等到一定数量的线程都执行了barrier.await()才能继续往后执行,就想像栅栏一样每集齐一定数量的人才能把栅栏压倒,到齐的每个人才能冲出去继续往后跑,这一批次人跑了栅栏就又恢复了,还得等到这个数量的人才能压倒栅栏再跑出去,如此往复。

public class Day05 extends Thread {
    public static void main(String[] args) {
        AtomicInteger count = new AtomicInteger();
        CyclicBarrier barrier = new CyclicBarrier(20, () -> {
            synchronized (o){
                System.out.println("满人,发车");
            }
        });
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3.Phaser

Phaser像多到栅栏一样,将运行的代码分为几个区间,每个区间都必须等到足够的数量之后才能继续往后跑。需要自己继承Phaser重写onAdvance方法进行具体处理。可以控制栅栏上数量及栅栏上等待线程的数量。

public class Day05 extends Thread {
    static Random r = new Random();
    static MarriagePhaser phaser = new MarriagePhaser();
    //static MarriagePhaser2 phaser2 = new MarriagePhaser2();


    static void milliSleep(int milli) {
        try {
            TimeUnit.MILLISECONDS.sleep(milli);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        phaser.bulkRegister(7);
        //phaser2.bulkRegister(8);

        for(int i=0; i<5; i++) {

            new Thread(new Person("p" + i)).start();
        }
        new Thread(new Person("新郎")).start();
        new Thread(new Person("新娘")).start();

    }
    static class MarriagePhaser extends Phaser {
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {

            switch (phase) {
                case 0:
                    System.out.println("所有人到齐了!" + registeredParties);
                    System.out.println();
                    return false;
                case 1:
                    System.out.println("所有人吃完了!" + registeredParties);
                    System.out.println();
                    return false;
                case 2:
                    System.out.println("所有人离开了!" + registeredParties);
                    System.out.println();
                    return false;
                case 3:
                    System.out.println("婚礼结束!新郎新娘抱抱!" + registeredParties);
                    return true;
                default:
                    return true;
            }
        }
    }
//    static class MarriagePhaser2 extends Phaser {
//        @Override
//        protected boolean onAdvance(int phase, int registeredParties) {
//
//            switch (phase) {
//                case 0:
//                    System.out.println("婚礼结束!新郎新娘抱抱!" + registeredParties);
//                    return true;
//                default:
//                    return true;
//            }
//        }
//    }
    
    static class Person implements Runnable {
        String name;

        public Person(String name) {
            this.name = name;
        }

        public void arrive() {

            milliSleep(r.nextInt(1000));
            System.out.printf("%s 到达现场!\n", name);
            phaser.arriveAndAwaitAdvance();
        }

        public void eat() {
            milliSleep(r.nextInt(1000));
            System.out.printf("%s 吃完!\n", name);
            phaser.arriveAndAwaitAdvance();
        }

        public void leave() {
            milliSleep(r.nextInt(1000));
            System.out.printf("%s 离开!\n", name);


            phaser.arriveAndAwaitAdvance();
        }
        private void hug() {
            if(name.equals("新郎") || name.equals("新娘")) {
                milliSleep(r.nextInt(1000));
                System.out.printf("%s 洞房!\n", name);
                phaser.arriveAndAwaitAdvance();
                //phaser2.arriveAndAwaitAdvance();
            } else {
                phaser.arriveAndDeregister();
                //phaser2.arriveAndDeregister();
                //phaser.register()
            }
        }
        @Override
        public void run() {
            arrive();
            eat();
            leave();
            hug();
        }
    }
}

在这里插入图片描述

4.ReadWriteLock

读写锁其实就是:互斥锁和排它锁。
代码示例如下:

public class Day05 extends Thread {
    static Lock lock = new ReentrantLock();
    private static int value;

    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();

    public static void read(Lock lock) {
        try {
            lock.lock();
            Thread.sleep(1000);
            System.out.println("read over!");
            //模拟读取操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void write(Lock lock, int v) {
        try {
            lock.lock();
            Thread.sleep(1000);
            value = v;
            System.out.println("write over!");
            //模拟写操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }





    public static void main(String[] args) {
        //Runnable readR = ()-> read(lock);
        Runnable readR = ()-> read(readLock);

        //Runnable writeR = ()->write(lock, new Random().nextInt());
        Runnable writeR = ()->write(writeLock, new Random().nextInt());

        for(int i=0; i<18; i++) new Thread(readR).start();
        for(int i=0; i<2; i++) new Thread(writeR).start();
        
    }
}

5.Semaphore

semaphore就好像多辆车过收费站,如果设置的收费站只有两个,那么同时过收费站的只能有两个。或者说,就好像进网吧登记一样,很多人进网吧上网,但笔只有两支,可以同时有很多人进来,但只有两个拿到笔的人才能进行登记,登记完后将笔放回,其他到来的人才能拿到笔进行登记。当两个笔都被拿走时,其他人只能阻塞等待。而登记过后能同时上网的人数自然不受限制。

public class Day05 {
    public static void main(String[] args) {
        //Semaphore s = new Semaphore(2);
        Semaphore s = new Semaphore(2, true);
        //允许一个线程同时执行
        //Semaphore s = new Semaphore(1);

        new Thread(()->{
            try {
                s.acquire();

                System.out.println("T1 running...");
                Thread.sleep(200);
                System.out.println("T1 running...");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                s.release();
            }
        }).start();

        new Thread(()->{
            try {
                s.acquire();

                System.out.println("T2 running...");
                Thread.sleep(200);
                System.out.println("T2 running...");

                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

6.Exchanger

线程之间交换数据使用,他是阻塞的。且Exchanger只能是两个线程之间的
在这里插入图片描述
代码示例如下:

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

7.LockSupport

public class Day05 {
    public static void main(String[] args) {
        Thread t = 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();
                }
            }
        });
        t.start();
        LockSupport.unpark(t);
//        try {
//            TimeUnit.SECONDS.sleep(8);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        System.out.println("after 8 seconds");
//        LockSupport.unpark(t);
    }
}

他的执行过程,unpark()是可以在park()之前就指定,依然可以生效。而在wait()之前是不可以notify()的,是无效操作。

在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值