zoukankan      html  css  js  c++  java
  • 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.令牌桶算法

    基本思想
    	在令牌桶中,存放的将不是请求,而是令牌,处理程序只有拿到令牌,才能对请求进行处理,如果没有令牌,处理程序就要丢弃请求或者等待可用令牌,为了限制流速,该算法在每个单位时间内产生一定量的令牌存入桶中。
    
  • 相关阅读:
    深入理解计算机系统 第六章 存储器层次结构 第二遍
    深入理解计算机系统 第六章 存储器层次结构
    深入理解计算机系统 第八章 异常控制流 Part2 第二遍
    深入理解计算机系统 第八章 异常控制流 part2
    深入理解计算机系统 第八章 异常控制流 Part1 第二遍
    深入理解计算机系统 第八章 异常控制流 part1
    深入理解计算机系统 第三章 程序的机器级表示 Part2 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part2
    深入理解计算机系统 第三章 程序的机器级表示 Part1 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part1
  • 原文地址:https://www.cnblogs.com/INnoVationv2/p/13021135.html
Copyright © 2011-2022 走看看