Skip to content
yangyp8110 edited this page Jan 17, 2018 · 1 revision

ReentrantLock(重入锁),在等待锁的过程中可以中断(可以灵活控制锁范围;可以中断)

public class test1Class {
    public static class ReenterLock implements Runnable{
        public static ReentrantLock lock = new ReentrantLock();
        public static int i = 0;

        @Override
        public void run() {
            for (int j = 0; j < 100000; j++) {
                lock.lock();//仅作演示反复进入,不建议在循环内部使用锁
                try{
                    i++;
                }finally {
                    lock.unlock();
                }
            }
        }
    }
}

运行结果:
    public void test1() throws InterruptedException {
        //重入锁(可反复进入)
        test1Class.ReenterLock t1 = new test1Class.ReenterLock();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t1);

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        /**
         * 200000  --- 第一次执行
         * 400000  --- 第二次执行(刷新)
         * 600000  --- 第三次执行(刷新)
         * ……
         */
        System.out.println(test1Class.ReenterLock.i);
    }

锁申请等待限时 (预防死锁)

public class test2Class {
    public static class TimeLock implements Runnable{
        public static ReentrantLock lock = new ReentrantLock();

        @Override
        public void run() {
            try {
                /**
                 * tryLock 不带参数时:
                 *      如果锁未被其他线程占用,则申请锁成功,并立即返回true;
                 *      如果锁被其他线程占用,则当前线程不会进行等待,立即返回false,申请锁失败
                 *   所以tryLock不带参数并不会产生死锁
                 */
                if(lock.tryLock(5, TimeUnit.SECONDS)){//请求锁,等待5秒
                    Thread.sleep(6000);
                }else{
                    System.out.println(Thread.currentThread().getName() +" get lock failed");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if(lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }
}

运行结果:
    public void test2() {
        /**
         * 输出:(t1持有锁6秒,t2请求锁5秒未获取到)
          t2 get lock failed
         */
        test2Class.TimeLock timeLock = new test2Class.TimeLock();
        Thread t1 = new Thread(timeLock, "t1");
        Thread t2 = new Thread(timeLock, "t2");

        t1.start();
        t2.start();
    }

模拟死锁

public class test3Class {
    public static class IntLock implements Runnable {
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        public int lock;

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

        @Override
        public void run() {
            try {
                if (lock == 1) {
                    //持有锁,同时可以响应中断
                    lock1.lockInterruptibly();
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException ex) {
                    }
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + " : 线程执行完毕 !"+ lock);
                } else {

                    //持有锁,同时可以响应中断
                    lock2.lockInterruptibly();
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException ex) {
                    }
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + " : 线程执行成功 !" + lock);
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) {
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getName() + " : 线程退出 !");
            }
        }
    }
}

运行结果:
    public void test3() throws InterruptedException {
        /**
         * 模拟死锁(由于t2中断,t1获取lock2锁,继续执行)
         * 如果将 t2.interrupt(); 注释,程序陷入死锁,永远不能结束
         *
         * 输出:
         t2 : 线程退出 !
         java.lang.InterruptedException
         t1 : 线程执行完毕 !1
         at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
         t1 : 线程退出 !
         */
        test3Class.IntLock intLock1 = new test3Class.IntLock(1);
        test3Class.IntLock intLock2 = new test3Class.IntLock(2);

        Thread t1 = new Thread(intLock1, "t1");
        Thread t2 = new Thread(intLock2, "t2");

        t1.start();
        t2.start();

        Thread.sleep(2000);

        //中断一个线程(如果注释,程序进入死锁,永远不能结束)
        t2.interrupt();
    }

死锁解决办法:

 * 1:锁申请等待限时(test2)
 * 2:条件触发(test3)

死锁排查

  • a.输入 jps 命令; 可以查看java进程id,查看当前java进程(测试用例SpringBoot启动的是ApplicationMain)

    输出进程中找到 226776 ApplicationMain

  • b.输入 jstack 226776
             C:\Windows\system32>jstack 226776
             2017-06-07 23:01:40
             Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode):
             ****省略一部分***
             =========================
             学家B":
             iting to lock monitor 0x000000000bd9ed98 (object 0x00000000db70c140, a
             .Object),
             ich is held by "哲学家A"
             学家A":
             iting to lock monitor 0x000000000bd9d798 (object 0x00000000db70c150, a
             .Object),
             ich is held by "哲学家B"

             stack information for the threads listed above:
             ===============================================
             学家B":
             at concurrency.demo.projects.model.chapter3.DeadLock.run(DeadLock.

             - waiting to lock <0x00000000db70c140> (a java.lang.Object)
             - locked <0x00000000db70c150> (a java.lang.Object)
             学家A":
             at concurrency.demo.projects.model.chapter3.DeadLock.run(DeadLock.

             - waiting to lock <0x00000000db70c150> (a java.lang.Object)
             - locked <0x00000000db70c140> (a java.lang.Object)

             d 1 deadlock.

公平锁 ReentrantLock(true) ; 非公平锁 ReentrantLock() --默认非公平锁

重入锁 Condition(条件)

  • Condition与Object.wait()、Object.notify()相似,wait和notify是和synchronized搭配,Condition是与ReentrantLock搭配使用

Semaphore(信号量)

/**
 * Created by mr.yang on 2017/6/6.
 *
 * 以下例子每次执行3个线程(每次允许3个线程访问资源,释放(semaphore.release())掉后可以有线程允许访问)
 */
public class test4Class {
    public static class SemapDemo implements Runnable{
        final Semaphore semaphore = new Semaphore(3);

        @Override
        public void run() {
            try {
                //申请信号量(如果未申请到,则等待)
                semaphore.acquire();

                Thread.sleep(2000);
                System.out.println("id:"+Thread.currentThread().getId()+",name:"+Thread.currentThread().getName()+": done !");

                //释放信号量
                semaphore.release();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
    public void test4(){
        /**
         * 信号量 (Semaphore)
         *    对锁的扩展,锁一次只允许一个线程访问一个资源;而信号量可以指定多个线程同时访问一个资源
         *
         * 以下例子每次执行3个线程(每次允许3个线程访问资源,释放(semaphore.release())掉后可以有线程允许访问)
         */
        ExecutorService executor = Executors.newFixedThreadPool(20);
        test4Class.SemapDemo demo = new test4Class.SemapDemo();
        for (int i = 0; i < 20; i++) {
            executor.submit(demo);
        }

        System.out.println("finished");
    }

ReadWriteLock(读写锁)

CountDownLatch(倒计时) countDown.countDown() : 倒计时递减 countDown.await() : 等待倒计时归0 //用于检查步骤都完成了

CyclicBarrier(循环栅栏) 每完成一批计数,计数器会归0,然后执行任务;紧接着进行下一批计数,循环往复

线程池(Executors线程池工厂)

  • newFixedThreadPool 创建固定数量的线程池,线程数量始终不变。任务提交到线程池,如有空闲线程,则立即执行;没有空闲线程,会将任务保存在一个任务队列中,等有空闲 线程时,再处理空闲队列中的任务
  • newSingleThreadExecutor 返回只有一个线程的线程池。任务提交到线程池,如有空闲线程,则立即执行;没有空闲线程,会将任务保存在一个任务队列中,等有空闲 线程时,再处理空闲队列中的任务
  • newCachedThreadPool 返回可以根据实际情况调整线程数量的线程池。线程池的线程数量不确定。任务提交到线程池,如有空闲线程可复用,则优先使用可复用的线程; 没有空闲线程,会创建新的线程处理任务,处理完成后会返回线程池进行复用。
  • newSingleThreadScheduledExecutor 返回ScheduledExecutorService对象,线程池大小为1,ScheduledExecutorService接口在ExecutorService接口之上扩展了 在给定时间执行某任务的功能(在某个固定的延时之后执行 或 周期性执行某个任务)
  • newScheduledThreadPool 返回ScheduledExecutorService对象,并且可以指定线程池的线程数量
    • 定时任务(ScheduledExecutorService不会造成任务堆叠)

      schedule : 在给定时间对任务进行一次调度

      scheduleAtFixedRate :周期性调度(如果执行任务时间 8s 调度时间 2s 则任务会8s一次频率调用)

      scheduledWithFixedDelay :间隔时间调用(在上一次调用结束后,间隔指定时间调用下一次任务)

实际上是对ThreadPoolExecutor的封装,ThreadExecutor超负载了的拒绝策略:

Fork/Join框架

  • 可以通过ForkJoinPool的submit方法向ForkJoinPool线程池提交一个ForkJoinTask任务
  • ForkJoinTask有两个重要的子类:
    • RecursiveAction(没有返回值的任务)
    • RecursiveTask(有返回值的任务)
Clone this wiki locally