高并发编程三

来源:互联网 发布:豆瓣2017网络剧top10 编辑:程序博客网 时间:2024/04/29 07:22
  1. 重入锁
    重入锁可以完全替代 synchronized. JDK 5.0,重入锁性能好于 synchronized. JDK 6.0 开始,两者差距不大。

    重入锁使用 java.util.concurrent.locks.ReenitrantLock 实现。

public class ReentrantLockTest implements Runnable {            static ReentrantLock reentrantLock = new ReentrantLock();            static int i = 0;            @Override            public void run() {                for (int j = 0;j < 10000;j++) {                    //加个重入锁                    reentrantLock.lock();                    try {                        i++;                    } finally {                        reentrantLock.unlock();                    }                }            }            public static void main(String[] args) throws InterruptedException {                ReentrantLockTest reentrantLockTest = new ReentrantLockTest();                /*Thread t1 = new Thread(new ReentrantLockTest());                Thread t2 = new Thread(new ReentrantLockTest());*/                Thread t1 = new Thread(reentrantLockTest);                Thread t2 = new Thread(reentrantLockTest);                t1.start();t2.start();                t1.join();t2.join();                System.out.println(i);            }        }
    重入锁中,不必考虑加锁的对象是否是同一个对象。    与 synchronized 相比,重入锁有显著的操作过程。    开发人员要手动指定何时加锁,何时释放锁。    一个线程连续两次获得同一把锁,这是允许的。加多少次的锁,要释放相同次数的锁。1.1 中断响应对于 synchronized,如果一个线程在等待,那么结果只有两种情况,要么获得这把锁,那么保存等待。使用重入锁,提供另一种可能,就是线程可以被中断。
public class InterruptionLockTest implements Runnable {        static ReentrantLock lock1 = new ReentrantLock();        static ReentrantLock lock2 = new ReentrantLock();        int lock;        public InterruptionLockTest(int lock) {            this.lock = lock;        }        @Override        public void run() {            try {                if (lock == 1) {                    System.out.println("线程1 加 lock1 中断锁");                    lock1.lockInterruptibly();                    System.out.println("线程1 加完 lock1 中断锁");                    Thread.sleep(1000);                    System.out.println("线程1 加 lock2 中断锁");                    lock2.lockInterruptibly();                    System.out.println("线程1 加完 lock2 中断锁");                } else {                    System.out.println("线程2 加 lock2 中断锁");                    lock2.lockInterruptibly();                    System.out.println("线程2 加完 lock2 中断锁");                    Thread.sleep(2000);                    System.out.println("线程2 加 lock1 中断锁");                    lock1.lockInterruptibly();                    System.out.println("线程2 加完 lock1 中断锁");                }            } catch (InterruptedException e) {                if (lock1.isHeldByCurrentThread()) {                    System.out.println("lock1 释放锁");                    lock1.unlock();                }                if (lock2.isHeldByCurrentThread()) {                    System.out.println("lock2 释放锁");                    lock2.unlock();                }            }        }        public static void main(String[] args) throws InterruptedException {            InterruptionLockTest lockTest1 = new InterruptionLockTest(1);            InterruptionLockTest lockTest2 = new InterruptionLockTest(2);            Thread t1 = new Thread(lockTest1);            Thread t2 = new Thread(lockTest2);            t1.start();t2.start();            Thread.sleep(5000);            System.out.println("线程2 中断");            t2.interrupt();        }    }
执行结果:线程1 加 lock1 中断锁线程1 加完 lock1 中断锁线程2 加 lock2 中断锁线程2 加完 lock2 中断锁线程1 加 lock2 中断锁线程2 加 lock1 中断锁线程2 中断lock2 释放锁线程1 加完 lock2 中断锁1.2 锁申请等待限时避免死锁一种方法,等待限时。使用 tryLock() 进行一次限时等待。
public class LimitedTimeLock implements Runnable {        static ReentrantLock reentrantLock = new ReentrantLock();        @Override        public void run() {            try {                System.out.println(Thread.currentThread().getId() + " 进入");                if (reentrantLock.tryLock(5, TimeUnit.SECONDS)) {                    System.out.println(Thread.currentThread().getId() + " 执行");                    Thread.sleep(5000);                } else {                    System.out.println(Thread.currentThread().getId() + " 等待超时");                    System.out.println("get lock fail");                }            } catch (InterruptedException e) {                e.printStackTrace();            } finally {                if (reentrantLock.isHeldByCurrentThread()) {                    reentrantLock.unlock();                }            }        }        public static void main(String[] args) {            LimitedTimeLock limitedTimeLock = new LimitedTimeLock();            Thread t1 = new Thread(limitedTimeLock);            Thread t2 = new Thread(limitedTimeLock);            t1.start();t2.start();        }    }
执行结果    11 进入    12 进入    11 执行    12 等待超时    get lock fail1.3公平锁    公平锁会按照时间的先后顺序,保证先到者先得,后到者后得。    特点是:不会产生饥饿现象。    重入锁有一个如下的构造函数:        public ReentrantLock(boolean fair)    当参数 fair 为 true 时,表示锁是公平的。公平锁看起来很优美,但是要实现公平锁必然要求系统维护一个有序队列,因此公平锁在实现成本比较高,性能相对也非常低下。    默认情况下,锁是非公平的。
public class FairLock implements Runnable {            //声明一个公平锁            static ReentrantLock lock = new ReentrantLock(true);            @Override            public void run() {                while (true) {                    try {                        lock.lock();                        System.out.println(Thread.currentThread().getName() + " 获得锁");                    } finally {                        lock.unlock();                    }                }            }            public static void main(String[] args) {               /* FairLock fairLock = new FairLock();                FairLock fairLock1 = new FairLock();                Thread t1 = new Thread(fairLock);                Thread t2 = new Thread(fairLock1);*/                FairLock fairLock = new FairLock();                Thread t1 = new Thread(fairLock,"线程_1");                Thread t2 = new Thread(fairLock,"线程_2");                t1.start();t2.start();            }        }
    执行结果        线程_2 获得锁        线程_1 获得锁        线程_2 获得锁        线程_1 获得锁        线程_2 获得锁        线程_1 获得锁    ReentrantLock的几个重要方法整理:    1) lock() 获得锁,如果锁已经被占用,则等待    2) lockInterruptibly() 获得锁,但优先响应中断    3) tryLock() 尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回    4) tryLock(long time,TimeUnit unit) 在给定时间内尝试获得锁    5) unLock() 释放锁。
  1. condition 条件
    condition 可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。

    condition 接口提供的基本方法:

void await() throws InterruptedException    void awaitUninterruptibly()    long awaitName(long nanosTimeout) throws InterruptedException    boolean await(long time,TimeUnit unit) throws InterruptedException    boolean awaitUntil(Date deadline) throws InterruptedException    void signal();    void signalAll()
1) await() 使当前线程等待,同时释放当前锁,当其他线程中使用 signal() 或者 signalAll()线程会重新获得锁并继续执行。和 Object.wait() 相似2) awaitUninterruptibly() 和 await() 基本相同,但它并不会在等待过程中响应中断。3) singal() 用于唤醒一个在等待中的线程。和 Object.notify() 类似。
  1. 信号量(semaphore)
    信号量为多线程协作提供了更强大的控制方法。广义上,信号量是对锁的扩展。
    无论是 synchronized 还是 reentrantLock,一次只允许一个线程访问一个资源,
    信号量可以指定多个线程,同时访问某一个资源。

    构造函数:

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

    构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。

    信号量的主要逻辑方法:

    public void acquire()public void acquireUninterruptibly()public boolean tryAcquire()public boolean tryAcquire(long timeout,TimeUnit unit)public void release()1) acquire() 尝试获得一个准入的许可,若无法获得,线程会等待,直到有线程释放一个许可或者当前线程被中断。2) acquireUninterruptibly() 和 acquire 类似,但不响应中断。3) tryAcquire() 尝试获得一个许可,如果成功返回true,失败返回false,不会进行等待。4) release() 用于线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。
public class SemapDemo implements Runnable {            final Semaphore semaphore = new Semaphore(5);            @Override            public void run() {                try {                    semaphore.acquire();                    Thread.sleep(5000);                    System.out.println(Thread.currentThread().getId() + " do");                    semaphore.release();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            public static void main(String[] args) {                ExecutorService ex = Executors.newFixedThreadPool(20);                final SemapDemo demo = new SemapDemo();                for (int i = 0;i < 20;i++) {                    ex.submit(demo);                }            }        }
    执行结果:    11 do    12 do    13 do    14 do    15 do    16 do    17 do    18 do    19 do    20 do    21 do    22 do    23 do    24 do    25 do    26 do    27 do    28 do    29 do    30 do    五个线程同时执行    申请信号量使用 acquire()操作,在离开时,务必使用 release() 释放信号量。    如果不幸发生了信号量的泄露,那么可以进入临界区的线程数量就会越来越少,    直到所有的线程均不可访问。
  1. ReadWriteLock 读写锁
    JDK5 中提供的读写分离锁。

    读写锁的访问约束情况

    读 + 读 = 非阻塞,读 + 写 = 阻塞
    写 + 读 = 阻塞, 写 + 写 = 阻塞

import java.util.Random;    import java.util.concurrent.locks.Lock;    import java.util.concurrent.locks.ReentrantLock;    import java.util.concurrent.locks.ReentrantReadWriteLock;    /**     * Created by wjp on 2016/12/30.     */    public class ReadWriteLockDemo {        private static Lock lock = new ReentrantLock();        private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();        private static Lock readLock = readWriteLock.readLock();        private static Lock writteLock = readWriteLock.writeLock();        private int value;        public Object handleRead(Lock lock) throws InterruptedException {            try {                lock.lock();                Thread.sleep(5000);                return value;            } finally {                lock.unlock();            }        }        public void handleWrite(Lock lock,int index) throws InterruptedException {            try {                lock.lock();                Thread.sleep(5000);                value = index;            } finally {                lock.unlock();            }        }        public static void main(String[] args) {            final ReadWriteLockDemo demo =  new ReadWriteLockDemo();            Runnable readRunnable = new Runnable() {                @Override                public void run() {                    try {                        //demo.handleRead(readLock);                        demo.handleRead(lock);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            };            Runnable writeRunale = new Runnable() {                @Override                public void run() {                    try {                        //demo.handleWrite(writteLock,new Random().nextInt());                        demo.handleWrite(lock,new Random().nextInt());                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            };            System.out.println("线程开始时间" + System.currentTimeMillis());            for (int i=0;i<18;i++) {                new Thread(readRunnable).start();            }            for (int i=18;i < 20;i++) {                new Thread(writeRunale).start();            }            System.out.println("线程结束时间" + System.currentTimeMillis());        }    }
使用 读写锁,和使用重入锁,在时间消耗上有明显的区别。

5倒计时器 countDownLatch
countDownLatch 是一个非常实用的多线程控制工具类。
这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再执行。

countDownLatch 的构造函数接收一个整数作为参数public CountDownLatch(int count)
import java.util.Random;    import java.util.concurrent.CountDownLatch;    import java.util.concurrent.ExecutorService;    import java.util.concurrent.Executors;    /**     * Created by wjp on 2016/12/30.     */    public class CountDownLatchDemo implements Runnable {        static final CountDownLatch end = new CountDownLatch(10);        static final CountDownLatchDemo demo = new CountDownLatchDemo();        @Override        public void run() {            //模拟检测任务            try {                Thread.sleep(new Random().nextInt(10) * 1000);                System.out.println("check complete");                end.countDown();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        public static void main(String[] args) throws InterruptedException {            ExecutorService ex = Executors.newFixedThreadPool(10);            for (int i =0;i < 10;i++) {                ex.submit(demo);            }            //等待检测            end.await();            System.out.println("fire!");            ex.shutdown();        }    }
执行结果:check completecheck completecheck completecheck completecheck completecheck completecheck completecheck completecheck completecheck completefire!

6. 循环栅栏 cyclicBarrier
多线程并发控制使用工具。和 CountDownLatch 非常类似,也可以实现线程间的计数等待,
但是它的功能比 CountDownLatch 更加复杂且强大。

public CyclicBarrier(int parties,Runable barrierAction)barrierAction : 当计数器一次计数完成后,系统会执行的动作。
import java.util.Random;    import java.util.concurrent.BrokenBarrierException;    import java.util.concurrent.CyclicBarrier;    /**     * Created by wjp on 2016/12/30.     */    public class CyclicBarrierBemo {        public static class Soldier implements Runnable {            private String soldier;            private final CyclicBarrier cyclicBarrier;            Soldier(CyclicBarrier cyclicBarrier,String soldierName) {                this.cyclicBarrier = cyclicBarrier;                this.soldier = soldierName;            }            @Override            public void run() {                //等待所有士兵集合                try {                    cyclicBarrier.await();                    doWork();                    //等待所有士兵完成工作                    cyclicBarrier.await();                } catch (InterruptedException e) {                    e.printStackTrace();                } catch (BrokenBarrierException e) {                    e.printStackTrace();                }            }            void doWork() {                try {                    Thread.sleep(Math.abs(new Random().nextInt() % 1000));                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(soldier + "任务完成");            }        }        public static class BarrierRun implements Runnable {            boolean flag;            int N;            public BarrierRun(boolean flag,int N) {                this.flag = flag;                this.N = N;            }            @Override            public void run() {                if (flag) {                    System.out.println("司令:[士兵" + N + "个,任务完成!]");                } else {                    System.out.println("司令:[士兵" + N + "个,集合完毕!]");                    flag = true;                }            }        }        public static void main(String[] args) {            final int N = 10;            Thread[] allSoldier = new Thread[N];            boolean flag = false;            CyclicBarrier cyclicBarrier = new CyclicBarrier(N,new BarrierRun(flag,N));            //设置屏障点,主要是为了执行这个方法            System.out.println("集合队伍!");            for (int i = 0;i < N;i++){                System.out.println("士兵" + i + "报道");                allSoldier[i] = new Thread(new Soldier(cyclicBarrier,"士兵" + i));                allSoldier[i].start();            }        }    }
集合队伍!士兵0报道士兵1报道士兵2报道士兵3报道士兵4报道士兵5报道士兵6报道士兵7报道士兵8报道士兵9报道司令:[士兵10个,集合完毕!]士兵3任务完成士兵6任务完成士兵5任务完成士兵7任务完成士兵2任务完成士兵1任务完成士兵9任务完成士兵0任务完成士兵4任务完成士兵8任务完成司令:[士兵10个,任务完成!]
  1. 线程阻塞工具类 LockSupport
    可以在线程内任意位置让线程阻塞。
    和 Thread.suspend() 相比,它弥补了由于 resume() 在前发生,导致线程无法继续执行的情况。
    和 Object.wait() 相比,它不需要先获得某个对象的锁,也不会抛出 InterruptedException

    LockSupport 的静态方法 park() 可以阻塞当前线程,类似的有 parkNanos(),parkUntil()
    实现了一个限时的等待。

public class LockSupportDemo {        public static Object u = new Object();        static ChangeObjectThread t1 = new ChangeObjectThread("t1");        static ChangeObjectThread t2 = new ChangeObjectThread("t2");        public static class ChangeObjectThread extends Thread {            public ChangeObjectThread(String name) {                super.setName(name);            }            @Override            public void run() {                System.out.println("in " + getName());                LockSupport.park();            }        }        public static void main(String[] args) throws InterruptedException {            t1.start();            Thread.sleep(100);            t2.start();            LockSupport.unpark(t1);            LockSupport.unpark(t2);            t1.join();            t2.join();        }    }
0 0
原创粉丝点击