码迷,mamicode.com
首页 > 其他好文 > 详细

synchronized和volatile以及ReentrantLock

时间:2020-04-20 23:38:27      阅读:64      评论:0      收藏:0      [点我收藏+]

标签:生产   重入   atom   sync   效率   finally   ext   run   连续   

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,再看下结果

技术图片

 

 

 

 

 


 

synchronized和volatile以及ReentrantLock

标签:生产   重入   atom   sync   效率   finally   ext   run   连续   

原文地址:https://www.cnblogs.com/lusaisai/p/12741311.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!