zoukankan      html  css  js  c++  java
  • synchronized和volatile以及ReentrantLock

    synchronized

    synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例

    锁定的对象有两种:1.类的实例 2.类对象

    加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。

    看个demo:

    private int count = 10;
        private Object object = new Object();
    
        public void test(){
            synchronized (object){
                count--;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
            }
        }

    synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例

    public class Demo2 {
    private int count = 10;

    public void test(){
    //
    synchronized (this){
    count--;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
    }
    }
    }

    直接加在方法声明上,相当于是synchronized(this)

    public class Demo3 {
    
        private int count = 10;
    
        //直接加在方法声明上,相当于是synchronized(this)
        public synchronized void test(){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }

    synchronize关键字修饰静态方法锁定的是类的.class对象

    静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class对象

    public class Demo4 {
    
        private static int count = 10;
    
        //synchronize关键字修饰静态方法锁定的是类的.class对象
        //静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class对象
        public synchronized static void test(){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void test2(){
            synchronized (Demo4.class){//这里不能替换成this
                count--;
            }
        }
    
    }

    锁定某对象o,如果o的属性发生改变,不影响锁的使用,但是如果o变成另外一个对象,则锁定的对象发生改变,应该避免将锁定对象的引用变成另外一个对象

    t2能否执行?

    public class Demo1 {
    
        Object o = new Object();
    
        public void test(){
            synchronized (o) {
                while (true) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }
    
        public static void main(String[] args) {
            Demo1 demo = new Demo1();
            new Thread(demo :: test, "t1").start();//启动t1
            try {
                TimeUnit.SECONDS.sleep(3);//睡眠3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread t2 = new Thread(demo :: test, "t2");//启动t2
            demo.o = new Object();
            //t2能否执行?
            t2.start();
        }
    
    }

    看下结果

    不要以字符串常量作为锁定的对象,在下面,test1和test2其实锁定的是同一个对象

    public class Demo2 {
    
        String s1 = "hello";
        String s2 = "hello";
    
        public void test1(){
            synchronized (s1) {
                System.out.println("t1 start...");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end...");
            }
        }
    
        public void test2(){
            synchronized (s2) {
                System.out.println("t2 start...");
            }
        }
    
        public static void main(String[] args) {
            Demo2 demo = new Demo2();
            new Thread(demo :: test1,"test1").start();
            //5秒后执行
            new Thread(demo :: test2,"test2").start();
        }
    
    }

    同步代码快中的语句越少越好,比较test1和test2,业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁,采用细粒度的锁,可以使线程争用时间变短,从而提高效率

    public class Demo3 {
    
        int count = 0;
    
        public synchronized void test1(){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count ++;
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void test2(){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {
                count ++;
            }
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }

    问题就在于线程重入的问题,第一个线程减了个1变成9了,还没打印,第二个线程又减了个1,第三个线程又减了个1,

    这时候虽然第一个线程只减了一个1但是却打印出来一个7(这里情况是不一定的),可以给方法加上synchronized

    public class Demo1 implements Runnable{
    
        private int count = 10;
    
        @Override
        public /*synchronized*/ void run() {
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
        
        public static void main(String[] args) {
            Demo1 demo = new Demo1();
            for (int i = 0; i < 5; i++) {
                new Thread(demo,"THREAD" + i).start();
            }
        }
    
    }

    看下结果

    相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。

    public class Demo2 implements Runnable{
    
        private int count = 10;
    
        @Override
        public synchronized void run() {
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。
                Demo2 demo = new Demo2();
                new Thread(demo,"THREAD" + i).start();
            }
        }
    
    }
    同步方法和非同步方法是否可以同时调用?可以
    public class Demo{
    
        public synchronized void test1(){
            System.out.println(Thread.currentThread().getName() + " test1 start...");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " test1 end...");
        }
    
        public void test2(){
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " test2");
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo :: test1,"test1").start();
            new Thread(demo :: test2,"test2").start();
        }
    
    }

    脏读问题:实际业务当中应该看是否允许脏读,不允许的情况下对读方法也要加锁

    public class Demo {
    
        String name;
        double balance;
    
        public synchronized void set(String name,double balance){
            this.name = name;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.balance = balance;
        }
    
        public /*synchronized*/ double getBalance(String name){
            return this.balance;
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(()->demo.set("huaan",100.0)).start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(demo.getBalance("huaan"));//
    
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(demo.getBalance("huaan"));
        }
    
    }

     把注释代码放开,看下结果

    一个同步方法调用另外一个同步方法,能否得到锁?可以,synchronized本身可支持重入

    public class Demo {
    
        synchronized void test1(){
            System.out.println("test1 start.........");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            test2();
        }
    
        synchronized void test2(){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test2 start.......");
        }
    
        public static void main(String[] args) {
            Demo demo= new Demo();
            demo.test1();
        }
    
    }

    重入锁的另外一种情况,继承

    public class Demo {
    
        synchronized void test(){
            System.out.println("demo test start........");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("demo test end........");
        }
    
        public static void main(String[] args) {
                new Demo2().test();
        }
    
    }
    
    class Demo2 extends Demo {
    
        @Override
        synchronized void test(){
            System.out.println("demo2 test start........");
            super.test();
            System.out.println("demo2 test end........");
        }
    
    }

    碰到异常的情况,如果处理,不会自动释放锁,所以T2不会执行。

    public class Demo {
    
        int count = 0;
    
        synchronized void test(){
            System.out.println(Thread.currentThread().getName() + " start......");
            while (true) {
                count ++;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 5) {//碰到异常的情况,如果处理,不会自动释放锁,所以T2不会执行。
                    try {
                        int i = 1/0;
                    }catch (Exception e){
                        System.out.println("出错了");
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            Demo demo11 = new Demo();
            Runnable r = () -> demo11.test();
            new Thread(r, "t1").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(r, "t2").start();
        }
    
    }

    碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。

    public class Demo {
    
        int count = 0;
    
        synchronized void test(){
            System.out.println(Thread.currentThread().getName() + " start......");
            while (true) {
                count ++;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 5) {
                    //碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。
                    int i = 1/0;
                }
            }
        }
    
        public static void main(String[] args) {
            Demo demo11 = new Demo();
            Runnable r = () -> demo11.test();
            new Thread(r, "t1").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(r, "t2").start();
        }
    
    }

    看下结果

    volatile

    volatile 关键字,使一个变量在多个线程间可见,使用volatile关键字,会让所有线程都会读到变量的修改值

    在下面的代码中,running是存在于堆内存的t对象中,当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个副本,,并不会每次都去读取堆内存,

    这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行

    public class Demo {
    
        /*volatile*/ boolean running = true;
    
        public void test(){
            System.out.println("test start...");
            while (running){//空循环
    
            }
            System.out.println("test end...");
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo :: test,"t1").start();//指令重排序?内存一致性?cpu空转?
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo.running = false;
        }
    
    }

    看下结果

     加上volatile再看下结果

    volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized或者说volatile保证不了原子性

    比如说第一个线程加到100了,还没往上加,另外一个线程来了,把100拿过来执行方法,

    然后第一个线程继续加到101,第二个线程也加到101,他两往回写都是101,线程不会管你加到哪儿了,虽然说加了2但是实际上只加了1.

    public class Demo {
    
        volatile int count = 0;
    
        public void test(){
            for (int i = 0; i < 10000; i++) {
                count ++;
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList();
            //永远加不到10万
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread-" + i));
            }
            threads.forEach((o)->o.start());
            threads.forEach((o)->{
                try {
                    o.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    
    }

    永远加不到10万

    相比较上一个例子,synchronized既保证了原子性又保证了可见性

    public class Demo {
    
        int count = 0;
    
        //相比较上一个例子,synchronized既保证了原子性又保证了可见性
        public synchronized void test(){
            for (int i = 0; i < 10000; i++) {
                count ++;
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList<Thread>();
    
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread-" + i));
            }
    
            threads.forEach((o)->o.start());
    
            threads.forEach((o)->{
                try {
                    o.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    
    }

    AtomicInteger

    一道面试题:多个atomic类连续调用能否构成原子性?

    public class Demo {
    
        AtomicInteger count = new AtomicInteger(0);
    
        //比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止
        //不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了
        public void test(){
            for (int i = 0; i < 10000; i++) {
                //count.incrementAndGet();//相当于count++
                if(count.get() < 1000){
                    count.incrementAndGet();//相当于count++
                }
            }
        }
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList();
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread-" + i));
            }
            threads.forEach((o)->o.start());
            threads.forEach((o)->{
                try {
                    o.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    
    }

    两个原子操作一起用的时候无法保证原子性

     看下另一种情况

    public class Demo {
    
        AtomicInteger count = new AtomicInteger(0);
    
        //比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止
        //不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了
        public void test(){
            for (int i = 0; i < 10000; i++) {
                count.incrementAndGet();//相当于count++
                /*if(count.get() < 1000){
                    count.incrementAndGet();//相当于count++
                }*/
            }
        }
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList();
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread-" + i));
            }
            threads.forEach((o)->o.start());
            threads.forEach((o)->{
                try {
                    o.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    
    }

    看结果

    一道面试题:实现一个容器,提供两个方法,add,size写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束线程2

    public class Container1 {
    
         List lists = new ArrayList();
    
        public void add(Object o){
            lists.add(o);
        }
    
        public int size(){
            return lists.size();
        }
    
        public static void main(String[] args) {
            Container1 c = new Container1();
    
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
    
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }," t1").start();
    
            new Thread(()->{
                while (true) {
                    if (c.size() == 5) {
                        break;
                    }
                }
                System.out.println("t2线程结束");
            }, "t2").start();
        }
    
    }

    这里list在两个线程之间不保证可见性,所以线程2始终结束不了

     list前面加上volatile

     volatile List lists = new ArrayList();

    看下结果

    wait()和notify()

    public class Container3 {
    
        volatile List lists = new ArrayList();
    
        public void add(Object o){
            lists.add(o);
        }
    
        public int size(){
            return lists.size();
        }
    
        public static void main(String[] args) {
            Container3 c = new Container3();
            Object lock = new Object();
    
            new Thread(()->{
                synchronized (lock) {
                    System.out.println("t2启动");
                    if (c.size() != 5) {
                        try {
                            lock.wait();//wait会释放锁
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2结束");
                }
            }," t2").start();
    
            new Thread(()->{
                System.out.println("t1启动");
                synchronized (lock) {
                    for (int i = 0; i < 10; i++) {
                        c.add(new Object());
                        System.out.println("add " + i);
    
                        if (c.size() == 5) {
                            lock.notify();//notify不会释放锁,要等t1结束后才执行
                        }
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1").start();
        }
    
    }

    wait会释放锁,notify不会释放锁,要等t1结束后才执行

    相比较上一个例子,这里T1里面用wait释放锁,T2能够及时结束

    public class Container4 {
    
        volatile List lists = new ArrayList();
    
        public void add(Object o){
            lists.add(o);
        }
    
        public int size(){
            return lists.size();
        }
    
        public static void main(String[] args) {
            Container4 c = new Container4();
            Object lock = new Object();
    
            new Thread(()->{
                synchronized (lock) {
                    System.out.println("t2启动");
                    if (c.size() != 5) {
                        try {
                        lock.wait();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2结束");
                    lock.notify();
                }
            }," t2").start();
    
            new Thread(()->{
                System.out.println("t1启动");
                synchronized (lock) {
                    for (int i = 0; i < 10; i++) {
                        c.add(new Object());
                        System.out.println("add " + i);
                        if (c.size() == 5) {
                            lock.notify();
                            try {
                                lock.wait();//要释放锁,T2才能得到锁得以执行
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "t1").start();
    
        }
    
    }

     但是代码看上去很傻,我们优化一下

    CountDownLatch

    使用await和countdown方法替代wait和notify
    CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
    相当于是发令枪,运动员线程调用await等待,计数到0开始运行
    当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了

    public class Container5 {
    
        volatile List lists = new ArrayList();
    
        public void add(Object o){
            lists.add(o);
        }
    
        public int size(){
            return lists.size();
        }
    
        public static void main(String[] args) {
            Container5 c = new Container5();
    
            CountDownLatch latch = new CountDownLatch(1);
    
            new Thread(()->{
                System.out.println("t2启动");
                if (c.size() != 5) {
                    try {
                        latch.await();//准备
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("t2结束");
                }
            }," t2").start();
    
            new Thread(()->{
                System.out.println("t1启动");
                for (int i = 0; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
                    if (c.size() == 5) {
                        latch.countDown();
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "t1").start();
        }
    
    }

     面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法,能够支持两个生产者线程以及10个消费者线程的阻塞调用

    这里我们使用wait notifyAll来实现

    public class Container1<T>{
    
        private final LinkedList<T> lists = new LinkedList<>();
        private final int MAX = 10;
        private int count = 0;
        
        public synchronized void put(T t){
            while (lists.size() == MAX) {//
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            lists.add(t);
            ++count;
            this.notifyAll();
        }
        
        public synchronized T get(){
            T t = null;
            while (lists.size() == 0) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            t = lists.removeFirst();
            count--;
            this.notifyAll();
            return t;
        }
        
        public static void main(String[] args) {
            Container1<String> c = new Container1<>();
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    for (int j = 0; j < 5; j++) {
                        System.out.println(c.get());
                    }
                }, "c" + i).start();
            }
            
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            for (int i = 0; i < 2; i++) {
                new Thread(()->{
                    for (int j = 0; j < 25; j++) {
                        c.put(Thread.currentThread().getName() + "" + j);
                    }
                }, "p" + i).start();
            }
        }
    
    }
    这里探究为什么大多数情况下wait和while是一起使用的,
    * 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。
    * 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待
    * 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是
    * 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程
    * 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态,
    * 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if
    * 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了
    * 容器的容量。所以为了让他再一次判断,这里使用while

    我们优化一下:使用使用Lock和Condition来实现
    public class Container2<T> {
    
        private final LinkedList<T> lists = new LinkedList<>();
        private final int MAX = 10;
        private int count = 0;
        
        private Lock lock = new ReentrantLock();
        private Condition producer = lock.newCondition();
        private Condition consumer = lock.newCondition();
        
        public void put(T t){
            try {
                lock.lock();
                while (lists.size() == MAX) {
                    producer.await();//await()和signalAll()一起用
                }
                lists.add(t);
                ++count;
                consumer.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public T get(){
            T t = null;
            try {
                lock.lock();
                while (lists.size() == 0) {
                    consumer.await();
                }
                
                t = lists.removeFirst();
                count --;
                producer.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            
            return t;
        }
        
        public static void main(String[] args) {
            Container2<String> c = new Container2<>();
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    for (int j = 0; j < 5; j++) {
                        System.out.println(c.get());
                    }
                }, "c" + i).start();
            }
            
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            for (int i = 0; i < 2; i++) {
                new Thread(()->{
                    for (int j = 0; j < 25; j++) {
                        c.put(Thread.currentThread().getName() + " " + j);
                    }
                }, "p" + i).start();
            }
        }
    }

    ReentrantLock需要手动上锁和释放锁,

    condition就是在什么条件下怎么做

    对比上一个例子,Condition的方式可以更加精确的指定哪些线程被唤醒

    ReentrantLock

    reentrantlock用于替代synchronized,使用reentrantlock可以完成同样的功能

    * reentrantlock必须要手动释放锁
    * 使用synchronized锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,

    * 因此经常在finally中进行锁的释放

    public class RLDemo1 {
    
        Lock lock = new ReentrantLock();
    
        public void test1(){
            try {
                lock.lock();//this
                for (int i = 0; i < 3; i++) {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void test2(){
            lock.lock();
            System.out.println("test 2...");
            lock.unlock();
        }
    
        public static void main(String[] args) {
            RLDemo1 rlDemo1 = new RLDemo1();
            new Thread(rlDemo1::test1).start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(rlDemo1::test2).start();
        }
    
    }

    使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,

    * 线程可以决定是否继续等待
    * 可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
    * 可以根据tryLock的返回值来判定是否锁定
    * 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中

    public class RLDemo2 {
    
        Lock lock = new ReentrantLock();
    
        public void test1(){
            try {
                lock.lock();
                for (int i = 0; i < 2; i++) {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);//一直等着,锁不释放
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        /**
         * 使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,
         * 线程可以决定是否继续等待
         * 可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
         * 可以根据tryLock的返回值来判定是否锁定
         * 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
         */
        public void test2(){
            boolean locked = false;
            try {
                //locked = lock.tryLock();
                locked = lock.tryLock(3, TimeUnit.SECONDS);
                System.out.println("test2...." + locked);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (locked) {
                    System.out.println("test2 end");
                    lock.unlock();
                }
            }
        }
    
        public static void main(String[] args) {
            RLDemo2 rlDemo2 = new RLDemo2();
            new Thread(rlDemo2::test1).start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(rlDemo2::test2).start();
        }
    
    }

    使用lockInterruptibly来锁定可以对Interrupt方法作出响应,进入异常处理

    public class RLDemo3 {
    
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
    
            Thread t1 = new Thread(()->{
                try {
                    lock.lock();
                    System.out.println("t1 start");
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);//一直等着,锁不释放
                    System.out.println("t1 end");
                } catch (InterruptedException e) {
                    System.out.println("interrupted!");
                } finally {
                    lock.unlock();
                }
            });
            t1.start();
    
            Thread t2 = new Thread(()->{
                boolean locked = false;
                try {
    //                lock.lock();
                    //使用lockInterruptibly来锁定可以对Interrupt方法作出响应,进入异常处理
                    lock.lockInterruptibly();
                    System.out.println("t2 start");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println("t2 end");
                    locked = true;
                } catch (InterruptedException e) {
                    System.out.println("interrupted!");
                } finally {
                    if (locked){
                        lock.unlock();
                    }
                }
            });
            t2.start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            t2.interrupt();
        }
    
    }

    ReentrantLock可以指定是否为公平锁,true为公平,默认为false

    public class RLDemo4 extends Thread{
        //ReentrantLock可以指定是否为公平锁,true为公平,默认为false
        private static ReentrantLock lock = new ReentrantLock(false);
    
        @Override
        public void run() {
            for (int i = 0; i < 100 ; i++) {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "获得锁");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
        
        public static void main(String[] args) {
            RLDemo4 rlDemo4 = new RLDemo4();
            Thread t1 = new Thread(rlDemo4);
            Thread t2 = new Thread(rlDemo4);
            t1.start();
            t2.start();
        }
    
    }

     将其改为true,再看下结果

    
    
    
  • 相关阅读:
    陶哲轩实分析习题17.3.3
    陶哲轩实分析定理17.3.8 (二)
    《陶哲轩实分析》引理17.2.4证明_导数的唯一性
    陶哲轩实分析定理17.3.8(一)
    陶哲轩实分析定理17.3.8(一)
    《陶哲轩实分析》引理17.2.4证明_导数的唯一性
    键值对在架构设计里的应用
    来自Google、Amazon和Facebook等7大知名互联网的系统扩展经验
    对象的消息模型
    Google的系统工程师(SA)如何工作
  • 原文地址:https://www.cnblogs.com/lusaisai/p/12741311.html
Copyright © 2011-2022 走看看