JDK并发包一

JDK并发包一

同步控制是并发程序必不可少的重要手段,synchronized是一种最简单的控制方法。但JDK提供了更加强大的方法————重入锁

重入锁

重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。

ReentrantLock有几个重要的方法

  • lock()获得锁,如果锁已占用,则等待。
  • lockInterruptibly()获得锁,优先响应中断。
  • tryLock() 尝试获得锁若成功返回true 失败返回false 不等待立即返回
  • tryLock(long time,TimeUnit unit) 在给定的时间内进行等待
  • unlock()释放锁
public class MyTest {

    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new ReenterLock());
        Thread thread2 = new Thread(new ReenterLock());

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }


    public static class ReenterLock implements Runnable {

        @Override
        public void run() {
            for (int j = 0; j < 1000000; j++) {
                lock.lock();
                try {
                    i++;
                } finally {
                    lock.unlock();
                }


            }
        }
    }

}

与synchronized相比重入锁更加灵活 一个线程可以多次获取锁,获取多少次一定要释放多少次

中断响应

对于synchronized相比如果一个线程等待锁,只有两种情况,要么获得锁继续执行,要么等待。重入锁可以有另一种情况————中断thread2.interrupt()

 /**
     * 中断响应
     */
    public static class IntLock implements Runnable {
        int lock;
        public static ReentrantLock lock1=new ReentrantLock();
        public static ReentrantLock lock2=new ReentrantLock();

        public IntLock(int lock) {
            this.lock = lock;
        }


        @Override
        public void run() {
            try {

                if (lock==1){
                    lock1.lockInterruptibly();
                    Thread.sleep(500);
                    lock2.lockInterruptibly();
                }else {
                    lock2.lockInterruptibly();
                    Thread.sleep(500);
                    lock1.lockInterruptibly();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (lock1.isHeldByCurrentThread())
                lock1.unlock();
                if (lock2.isHeldByCurrentThread())
                lock2.unlock();
                System.out.println(Thread.currentThread().getName()+":线程退出");
            }

        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new IntLock(1));
        Thread thread2 = new Thread(new IntLock(2));
        thread1.start();
        thread2.start();
        Thread.sleep(2000);
        thread2.interrupt();


    }

thread2获得了thread1的锁thread1获得了thread2的锁两个线程相互等待。 程序形成死锁thread2.interrupt()执行后thread2中断 自动释放获得的所有锁,thread1可以继续执行。真正完成的只有thread1

锁申请等待限时

除了上面中断通过外部通知的方式避免死锁还有另一种方法,限时等待tryLock()

tryLock()方法有两个参数一个表示等待时长,一个是计时单位,在设置的时间内线程会请求获取锁,超过设置的时间还没获得锁就会返回false。获得成功就返回true

公平锁

在大多数情况下锁都是非公平的,即线程不是按照请求在锁不可用时锁的先后顺序获得锁的,系统只会从等待的线程中随机选一个,而公平锁会按照请求顺序在锁可用是分配锁。ReentrantLock lock1=new ReentrantLock(true);创建锁时在构造方法传入true时此锁就是公平锁,公平锁需要一个有序队列来实现因此成本较高性能低下没特殊要求一般不用。

Condition条件

使用synchronized进行通知就使用Object.wait() ,Object.notify() ,使用锁时Condition的方法几乎相同。

  • await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者signalAll()方法时线程会重现获得锁继续执行,当线程中断时也能跳出等待
  • awaitUninterruptibly()方法与await()基本相同,但是它并不会在等待过程中响应中断
  • singnal()方法用于唤醒一个等待中的线程,signalAll()唤醒所有线程
	public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        Thread thread = new Thread(new ReenterLockCondition());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.lock();
        condition.signal();
        lock.unlock();

    }

    public static class ReenterLockCondition implements Runnable {

        @Override
        public void run() {

            try {
                lock.lock();
                condition.await();
                System.out.println("线程开始执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }

        }
    }

在执行Conidition.await()之前要求当前线程必须持有相关的重入锁,执行signal()也一样也需要先持有重入锁,在singnal()执行后一般需要释放相关重入锁,否则唤醒的线程无法重新获得相关重入锁无法继续执行。

读写锁

读写锁是分离读操作与写(修改,删除,添加)的锁,可以更大程度提高性能。

非阻塞 阻塞
阻塞 阻塞

如果一个系统中有大量的读操作,那么读写锁就能发挥更大功效。

    public static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();//读写锁
    public static Lock read = readWriteLock.readLock();//读锁
    public static Lock write=readWriteLock.writeLock();//写锁

倒计时器

对于某个线程需要等待其他几个线程执行完毕自己再执行比较适用

假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

    public static final CountDownLatch end = new CountDownLatch(10);

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new CountDownLatchDemo());
        }
        try {
            end.await();
            System.out.println("准备完毕");
            executorService.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static class CountDownLatchDemo implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(new Random().nextInt(10)*1000);
                System.out.println(Thread.currentThread().getName()+"准备完毕");
                end.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

循环栅栏

CyclicBarrier实现的功能与CountDownLatch类似但刚强大,CyclicBarrier可以实现计数器的复用,

public class Soldier implements Runnable{


    private String soldier;
    private CyclicBarrier cyclicBarrier;


    public Soldier(String soldier, CyclicBarrier cyclicBarrier) {
        this.soldier = soldier;
        this.cyclicBarrier = cyclicBarrier;
    }

   public void doWork(){
       try {
           Thread.sleep(Math.abs(new Random().nextInt()%10000));

       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(this.soldier+"任务完成");
   }

    @Override
    public void run() {
        try {
            this.cyclicBarrier.await();
            doWork();
            this.cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

public class BarrierRun implements Runnable {

    private boolean flag;
    private int i;

    public BarrierRun(boolean flag, int i) {
        this.flag = flag;
        this.i = i;
    }

    @Override
    public void run() {
        if (this.flag) {
            System.out.println("任务完成");
        }else {
            System.out.println("集合完毕");
        }
    }
}

   public static void main(String[] args) {
        Thread[] threads=new Thread[10];
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new BarrierRun(false, 10));
        System.out.println("队伍集合");
        for (int i = 0; i < 10; i++) {
            System.out.println("士兵"+i+"报道");
            threads[i]=new Thread(new Soldier("士兵"+i,cyclicBarrier));
            threads[i].start();
        }
    }
原文地址:https://www.cnblogs.com/huangshen/p/13236128.html