呀?这就是锁(一)?

java常用锁

Synchroized(自旋锁,轻量级锁)

package com.LockDemo;

public class SynchronizedDemo {


    static Object o = new Object();

    //synchronized 修饰 m方法
    static synchronized void m1()  {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } //两个线程会等待,锁生效 此时锁定的对象是 m1 方法

    //synchronized 修饰对象
    void m2(){
        synchronized(this){
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }//两个线程会等待,锁生效 此时锁定的是 sd 对象

    //synchronized 修饰对象
    void m3(){
        synchronized(this){
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }//不等待,因为两个线程不争用对象资源

    static synchronized void m4(){
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }//如果有static 关键字,则会被加锁,反之,锁消失 ,证明在这种情况下,锁定的静态的 m4 方法,而非静态的 m4 每个属于每个线程特有的不加锁

    void m5(){
        synchronized (o){
            System.out.println(Thread.currentThread().getName());
            try {
                //o = new Object(); //o 指向一个新的对象 ,此时synchronized指向的o变成未加锁状态,第二个线程会直接进来,
                Thread.sleep(10000);
                o = new Object(); //此时再释放,则会等待10s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } //o在sleep前指向新的o,synchronized立刻失效,直接输出两个线程名字,反之则等待后输出

    public static void main(String[] args) {
        //m2 使用
        SynchronizedDemo sd = new SynchronizedDemo();
        new Thread(()->{
            /*
                m1(); //m1
                sd.m2(); //m2
                new SynchronizedDemo().m3(); //m3
             */
            new SynchronizedDemo().m5();
        }).start();

        //m4 第二种调用
        /*
        Runnable r = new Runnable() {
            @Override
            public void run() {
                new SynchronizedDemo().m4();
            }
        };
        new Thread(r).start();
        new Thread(r).start();
        */
        new SynchronizedDemo().m5(); //使用m4方式需要注释
    }

}

总结:

synchronized肯定会对一个对象加锁来实现并发,如果没有达到想要的目的,需要分析加锁对象是不是竞争线程都需要,如果你在TheadA给A加上了锁,但是ThreadB却不会使用A,那么这个锁就没有意义,并且在锁定过程中,对象不能更改,如果更改就相当于把锁加到了一个没有引用的对象上,没有意义,此时新的指向已经是无锁对象了,

备注:

异常会把锁丢失,所以需要异常处理严谨一些。

ReentrantLock (互斥锁,独占锁)

package com;

import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock和synchronized的基本用法相同,所以首先要理解synchronied的实现原理,
 * 这里主要讲述ReentranrLock相对于synchronized增强的部分
 */
public class ReeLockDemo {

    static ReentrantLock reeLock = new ReentrantLock();

    //普通加锁解锁
    static void m1(){
        try {
            //System.out.println(Thread.currentThread().getName()+"1"); //此处可以直接输出,即不加锁部分
            reeLock.lock();
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            reeLock.unlock(); //必须要消除锁
        }
    }//此时锁定的reeLock.lock()到reeLock.unlock()中间的部分


    //尝试加锁
    static void m2(){
        boolean locked = false;
        try {
            locked = reeLock.tryLock();
            if(locked){
                System.out.println(Thread.currentThread().getName());
                Thread.sleep(10000);
            }
            System.out.println(Thread.currentThread().getName()+locked); //这里输出Thread-0 false,因为此时的锁被main线程占用
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if(locked) reeLock.unlock(); //必须要消除锁,必须加上判断,不然如果没有获得锁便会直接报措,错误是
            /* Exception in thread "Thread-0" java.lang.IllegalMonitorStateException 锁状态不合法,因为没有锁,直接释放当然状态不对
            at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
            at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
            at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
             */
        }
    }

    //等待锁资源可以被打断了
    static void m3(){
        boolean locked = false;
        try {
            reeLock.lockInterruptibly();
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if(locked) reeLock.unlock();
        }
        /*
        输出结果
            main
            java.lang.InterruptedException
                at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
                at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
                at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
                at com.ReeLockDemo.m3(ReeLockDemo.java:53)
                at com.ReeLockDemo.lambda$main$0(ReeLockDemo.java:68)
                at java.lang.Thread.run(Thread.java:748)
                明显可以看到t1线程没有再等待锁,打断等待状态
         */
    }
    //真的公平吗?
    static void m4(){
        ReentrantLock fairLock = new ReentrantLock(true); //这里我们设定的是公平锁
        try {
            fairLock.lock();
            System.out.println(Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            fairLock.unlock(); //必须要消除锁

//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//
            //如果有sleep结果是这样的,节选一部分
            /*
            Thread-1
            Thread-0
            Thread-1
            Thread-0
            Thread-1
            Thread-0
             */
            //如果没有sleep结果是这样的,节选一部分
            /*
            Thread-0
            Thread-0
            Thread-0
            Thread-1
            Thread-1
            Thread-1
            Thread-1
             */
            /*
            从上面的结果不难发现,这个公平好像和想的不一样,这是因为,再系统内部是否公平是根据排队的顺序来的,即便是ThreadA刚刚被执行了一次,如果他第二次排队又排到
            ThreadB的前面,那么他还是会继续执行,这样想就容易明白了
             */
        }
    }

    public static void main(String[] args) {
        /*
        Thread t1 = new Thread(()->{
            //m1();
            m3();
        });//.start();

        //m1();
        //m2();

        //执行m3方法
        t1.start();
        //首先指向main中m3 ,2S 后释放
        try {
            m3();
            Thread.sleep(2000);
            t1.interrupt(); //t1不再等待锁
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         */
        //m5
        Thread t1 = new Thread(()->{
           while(true){
               m4();
           }
        });
        Thread t2 = new Thread(()->{
            while(true){
                m4();
            }
        });

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

    }
}

总结:

ReentrantLock和synchronized相比更加的灵活,并且需要显示的加锁和释放锁,所以也需要我们对它有更加深刻的理解,才能再使用的过程中有的放矢,有效的提升工作效率。

posted @ 2021-05-10 21:43  二五树  阅读(60)  评论(0编辑  收藏  举报