• 2.同步控制


    一、重入锁ReentrantLock

    1. 常用方法

    public static ReentrantLock lock = new ReentrantLock();
    lock.lock();//获得锁,如果锁已被占用,则等待
    lock.lockInterruptibly();//获得锁,但优先响应中断
    lock.tryLock();//尝试获得锁,若成功则返回true,若当前锁被其他线程占用,申请锁失败,则返回false,该方法不等待,因此也不会产生死锁
    lock.tryLock(long time, TimeUnit unit);//在给定时间内尝试获得锁
    lock.unlock();//释放锁
    

    2. 简单的重入锁使用案例

    public class Create implements Runnable {
        public static ReentrantLock lock = new ReentrantLock();
        public static int i = 0;
        @Override
        public void run() {
            for (int j = 0; j < 10000000; ++j) {
                lock.lock();
                try {
                    i++;
                } finally {
                    lock.unlock();
                }
            }
        }
        public static void main(String[] args) throws InterruptedException { }
    }
    

    3. 和synchronized的对比

    和synchronized相比,重入锁有明显的操作过程,开发人员必须手动指定何时加锁,何时释放,因此,重入锁对逻辑控制的灵活性高于synchronized。
    对于synchronized来说,若一个线程在等待锁,那么只会有两种情况
    	1.获得这把锁继续运行
    	2.保持等待
    但是使用重入锁lockInterruptibly,线程则可以在等待过程中被中断
    

    4. 重入的含义

    锁可以反复使用,一个线程可以连续两次获得同一把锁,但需要注意的是,如果同一个线程多次获得锁,那么也必须释放相同次数
        若释放次数多了,就会得到一个java.lang.IllegalMonitorStateException异常
        若释放次数少了,那么相当于线程还持有这个锁,其他线程无法进入临界区
    
    //重入锁实例
    lock.lock();
    lock.lock();
    try{
         i++;
    }finally {
        lock.unlock();
        lock.unlock();
    }
    

    5. 中断响应 lockInterruptibly

    //demo
    public class IntLock implements Runnable {
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lock;
        public IntLock(int lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            try {
                if (lock == 1) {
                    lock1.lockInterruptibly();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                    }
                    lock2.lockInterruptibly();
                } else {
                    lock2.lockInterruptibly();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                    }
                    lock1.lockInterruptibly();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread())
                    lock1.unlock();
                if (lock2.isHeldByCurrentThread())
                    lock2.unlock();
                System.out.println(Thread.currentThread().getId() + "线程退出");
            }
        }
        public static void main(String[] args) throws InterruptedException {
            IntLock r1 = new IntLock(1);
            IntLock r2 = new IntLock(2);
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);
            t1.start();
            t2.start();
            Thread.sleep(1000);
            t2.interrupt();
        }
    }
    //线程t1和t2启动后,t1先占用lock1,再占用lock2,t2先占用lock2,再占用lock1.因此两者很容易形成相互等待
    //而使用lockInterruptibly,在main函数中对t2进行中断,此时t2会放弃lock1的申请,同时释放已获得的lock2,然后t1就可以获得lock2继续执行下去
    

    6.锁申请等待限时tryLock

      //demo
    public class IntLock implements Runnable {
        public static ReentrantLock lock = new ReentrantLock();
        private static int i = 1;
        @Override
        public void run() {
            try {
                while (lock.isLocked()) {
                    System.out.println(Thread.currentThread().getId() + " 线程被阻塞,等待中,第" + i++ + "次尝试");
                    Thread.sleep(1000);
                }
                if (lock.tryLock(5, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread().getId() + " 获得线程,开始休眠");
                    Thread.sleep(6000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getId() + " 释放");
                if (lock.isHeldByCurrentThread()) lock.unlock();
            }
            System.out.println(Thread.currentThread().getId() + " 结束");
        }
        public static void main(String[] args) throws InterruptedException {
            IntLock r1 = new IntLock();
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r1);
            t1.start();
            Thread.sleep(1000);
            t2.start();
            t2.join();
            System.out.println("MISSION COMPLETE");
        }
    }
    

    7. 公平锁

    public static ReentrantLock lock = new ReentrantLock(boolean fair);
    public static ReentrantLock lock = new ReentrantLock(true);//公平锁
    
    在多数情况下,锁都是非公平的,例如线程1请求了锁A,然后线程2也请求获得锁A,非公平锁是随机分配给1或者2的,但是公平锁则是按照先到先得的理念分配锁,也就是先分配给1然后给2,
    
    公平锁的一大特点是不会产生饥饿,只要你排队就一定能获得资源。虽然公平锁看起来很优美,但是公平锁实现内部必然要维持一个有序队列,因而效率很低,因此若没有特别需求则不需要使用公平锁
    

    二、Condition

    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    通过lock接口获得一个Condition对象,就可以让线程在合适的时间等待,或在某特定时刻得到通知,继续执行。
    当线程使用 await()方法时,要求线程持有相关的重入锁,在 await()方法调用后,这个线程会释放这个锁,同理, signal()方法调用时,也要求线程先获得相关的锁,在调用完后,一定要释放锁,否则程序无法继续执行
    

    1.基本方法

    await();
    await(long time, TimeUnit unit);
    awaitNanos(long nanoTimeout);
    awaitUninterruptibly();
    awaitUntil(Date deadline);
    signal();//唤醒一个等待中的进程
    signalAll();//唤醒所有等待中的进程
    

    2.和wait()、notify()方法的异同

    wait()和notify()方法是与synchronized配合使用的,而Condition是与重入锁相关联的
    

    3.demo

    public class ReenterLockCondition implements Runnable {
        public static ReentrantLock lock = new ReentrantLock();
        public static Condition condition = lock.newCondition();
        @Override
        public void run() {
            try {
                lock.lock();//当线程使用await()方法时,要求线程持有相关的重入锁
                condition.await();//await()方法调用后,这个线程会释放这个锁
                System.out.println("Thread is going on");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            ReenterLockCondition r1 = new ReenterLockCondition();
            Thread t1 = new Thread(r1);
            t1.start();
            Thread.sleep(2000);
            lock.lock();//Condition.signal()方法调用时,要求线程先获得相关的锁
            condition.signal();
            lock.unlock();//在调用完后,一定要释放锁,否则t1无法继续执行
        }
    }
    

    三、信号量Semaphore

    信号量是对锁的扩展,无论是内部锁synchronized或重入锁ReentrantLock,一次都只允许一个线程访问资源,而信号量则可以指定多个线程同时访问某一个资源
    

    1.构造函数

    public Semaphore(int permits);
    public Semaphore(int permits, boolean fair);//第二个参数指定是否公平
    

    2.基本方法

    semp.acquire();//尝试获得准入许可,若无法获得则等待
    semp.acquireUninterruptibly();//和acquire相同,但是不响应中断
    semp.tryAcquire();//尝试获得,成功返回true,反之返回false,不阻塞
    semp.tryAcquire(long timeout,TimeUnit unit);
    public void release();//释放一个许可
    

    四、读写锁ReadWriteLock

    读不会修改文档,所以所有读线程之间不需要阻塞,而写线程会对文档进行修改,所以读写线程,写写线程之间需要进行阻塞。
    

    1.demo

    public class ReadWriteDemo {
        private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private static Lock readLock = readWriteLock.readLock();
        private static Lock writeLock = readWriteLock.writeLock();
        private int value;
        public Object handleRead(Lock lock) throws InterruptedException {
            try {
                lock.lock();
                Thread.sleep(1000);
                return value;
            } finally {
                lock.unlock();
            }
        }
        public void handleWrite(Lock lock, int index) throws InterruptedException {
            try {
                lock.lock();
                Thread.sleep(1000);
                value = index;
            } finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) {
            final ReadWriteDemo demo = new ReadWriteDemo();
            Runnable readRunnale = new Runnable() {
                @Override
                public void run() {
                    try {
                        demo.handleRead(readLock);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Runnable writeRunnale = new Runnable() {
                @Override
                public void run() {
                    try {
                        demo.handleWrite(writeLock, new Random().nextInt());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            for (int i = 0; i < 18; i++)
                new Thread(readRunnale).start();
            for (int i = 18; i < 20; i++)
                new Thread(writeRunnale).start();
        }
    }
    

    五、倒计数器CountDownLatch

    CountDownLatch的构造函数接受一个int作为参数,既这个计数器的计数个数 
    
    public class test implements Runnable {
        static final CountDownLatch latch = new CountDownLatch(10);
        static final test t = new test();
        @Override
        public void run() {
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newFixedThreadPool(10);
            for (int i = 0; i < 10; i++)
                exec.submit(t);
            latch.await();
            System.out.println("Fire");
            exec.shutdown();
        }
    }
    

    六、循环栅栏CyclicBarrier

    基础功能和倒计数器相似,可以实现线程间的计数等待,但是功能更加强大
    
    public CyclicBarrier(int parties,Runnable barrierAction);
    //parties:计数器个数
    //barrierAction:每次计数完成后,系统会执行的动作
    

    实例

    public class test {
        public static class Worker implements Runnable {
            private final CyclicBarrier cyclicBarrier;
            private final String id;
            Worker(CyclicBarrier cyclicBarrier, String id) {
                this.cyclicBarrier = cyclicBarrier;
                this.id = id;
            }
            @Override
            public void run() {
                try {
                    cyclicBarrier.await();
                    command();
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
            void command() {
                try {
                    Thread.sleep(new Random().nextInt(10) * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(id + " 号完成任务");
            }
        }
        public static class BarrierRun implements Runnable {
            private final int N;
            private boolean flag;
    
            public BarrierRun(boolean flag, int N) {
                this.N = N;
                this.flag = flag;
            }
            @Override
            public void run() {
                if (flag) {
                    flag = false;
                    System.out.println(N + "个工人,集合完毕");
                } else
                    System.out.println(N + "个工人,任务完成");
            }
        }
        public static void main(String[] args) {
            final int N = 5;
            Thread[] workers = new Thread[N];
            CyclicBarrier cyclic = new CyclicBarrier(N, new BarrierRun(true, N));
            for (int x = 0; x < N; x++) {
                System.out.println("工人" + x + "报道");
                workers[x] = new Thread(new Worker(cyclic, Integer.toString(x)));
                workers[x].start();
            }
        }
    }
    

    七、线程阻塞工具LockSupport

    LockSupport是一个线程阻塞工具,可以在线程内任意位置让线程阻塞。
    和Thread.suspend()方法相比,他弥补了由于resume()方法执行位置错误引发的异常
    和Object.wait()方法相比,他不需要先获得某个对象的锁,也不会抛出InterruptedException异常
    

    1.suspend、resume引发的异常

    //案例
    public class test {
        public static final Object u = new Object();
        public static class Change extends Thread {
            @Override
            public void run() {
                synchronized (u) {
                    System.out.println("first");
                    Thread.currentThread().suspend();
                }
            }
        }
        public static void main(String[] args) {
            Change change = new Change();
            change.start();
            change.resume();
        }
    }
    //当我们执行这段代码时,程序会输出first,但是程序不会退出,而是会挂起,这是因为时间顺序的缘故,resume发生在了suspend的前面,导致resume方法未起效。
    
    当我们用LockSupport改写上述程序,虽然我们仍然无法保证unpark发生在park前,但程序将可以正常运转退出。
    这是因为LockSupport使用了类似信号量的机制,他为每一个线程准备了一个许可证,默认状况下许可证为false,park为消费许可证,而unpark为使许可证可用,但是与信号量不同,许可证不能累加
    
    //改写后的程序
    public class test {
        public static final Object u = new Object();
        public static class Change extends Thread {
            @Override
            public void run() {
                synchronized (u) {
                    System.out.println("first");
                    LockSupport.park();
                }
            }
        }
        public static void main(String[] args) {
            Change change = new Change();
            change.start();
            LockSupport.unpark(change);
        }
    }
    

    2.中断

    与其他支持中断函数不同,LockSupport.park()方法不会抛出InterruptedException异常,但是可以从Thread.interrupted()获得中断标记。
    

    八、限流算法

    1.漏桶算法

    基本思想
    	利用一个缓存区,当有请求进入系统,无论请求速率如何,都先在缓存区中保存,然后以固定的流速流出缓存区
    特点
    	无论外部压力如何,漏桶算法总是以固定的流速处理数据
    

    2.令牌桶算法

    基本思想
    	在令牌桶中,存放的将不是请求,而是令牌,处理程序只有拿到令牌,才能对请求进行处理,如果没有令牌,处理程序就要丢弃请求或者等待可用令牌,为了限制流速,该算法在每个单位时间内产生一定量的令牌存入桶中。
    
  • 相关阅读:
    wamp支持win10吗?怎么设置?
    帝国cms添加修改会员字段时字段名不能带数字,否则注册页会出现空白
    微信用户量破6.5亿 首超移动QQ
    如何进行网站统计分析?分8步走!
    《如何策划一个有逼格的竞价专题页面》有感
    dedecms批量删除文档关键词可以吗
    一只刚学竞价两周的菜鸟
    Android/iOS微信6.3.5同时发布更新 支持群视频聊天、群公告
    帝国cms修改栏目后文章列表的url错误怎么解决
    dedecms批量导出新增文章url和标题
  • 原文地址:https://www.cnblogs.com/INnoVationv2/p/13021135.html
Copyright © 2020-2023  润新知