synchronized与Lock的区别用法!

提到锁,首先要明确一个概念:
1.线程与进程?
    一个简单的程序,最少需要一个进程而一个进程最少需要线程,所以关系是线程-->进程-->程序的大致构成结构,所以线程是
执行程序的最下单元,而进程是系统进行资源分配和调度的独立单元,所以以下是我们讨论的都是建立在线程的基础上

2.Thread的几个重要方法?
    a.start()方法,调用该方法开始执行线程
    b.stop()方法,调用该方法强制结束该线程执行
    c.join()方法,调用该方法等待线程结束
    d.sleep()方法,调用该方法线程进入等待
    e.run()方法,调用该方法直接执行线程的run()方法,这里调用start方法也可以运行线程,区别就是一个是由线程调度运行run()方法
    一个是直接调用了线程中的run()方法
    f.wait()方法和notify()方法呢?
            其实wait()方法和notify都是object()方法,不是thread方法
    g.wait()方法和notify()方法的区别?
            简单的来说wait()会释放锁对象而sleep()不会释放锁对象
            
3.线程的状态?
    新建状态:新建线程对象,并没有调用start()方法之前
    就绪状态:调用start()方法之后线程进入就绪状态,但是并不是说只要调用start()方法现象就能马上变为当前线程
    在变成当前线程之前都是就绪状态,值得一提的是,线程在睡眠和挂起中恢复的时候也会进入就绪状态
    运行状态:线程被设置为当前线程状态开始执行run()方法,就是就绪状态
    阻塞状态:线程被暂停,比如说调用sleep()方法后,线程就进入阻塞状态
    死亡状态:线程执行结束
    


4.锁的类型?
    可重入锁:在执行对象中,所有同步方法不用再次获得锁
    可中断锁:在等待获取锁的过程中可以中断
    公平锁:     按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁的权利
    读写锁:     对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步写
    

 5. synchronized与Lock的区别?

类别 synchronized Lock
存在层次 java的关键字,在jvm上 是一个借口
锁的释放

1.已获取锁的线程执行完同步代码快

2.线程执行发生异常,jvm会让线程释放锁

在finally中必须释放锁,不然容易造成死锁
锁的获取 假设A线程获得锁,B线程等,如果A线程阻塞.B线程一直等待 分情况而定,lock有多个获取锁的方式,具体下面会说,大致就可以尝试获得锁,线程就可以一直等待
锁的状态 无法判断 可以判断
锁的类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可)
性能 少量同步 大量同步 

6.lock详解 (打开 idea ctlr+n 输入lock命令找到lock 源码)

lock接口中我们可以看出主要的方法,这些方法的功能从注释可以看出

lock():获取锁,如果锁被暂用则一直等待
unlock():释放锁
tryLock():注意返回类型是boolean,如果获取锁的时候被占用,则返回false,否则返回true
trylock(long time,timeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事

通过以上的解释,大致可以解释在上个部分 "锁的类型(lockInterruptibly())","锁状态(tryLock())" 等问题

来看下这边在网上找到的例子(下面是Lock一般使用的例子,注意ReentrantLock是Lock接口的实现。):

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by :Corey
 * 17:50 2019/4/8
 */
public class lockMethod {

    private Lock lock = new ReentrantLock();

    //需要参与同步方法
    private void method(Thread thread) {
        lock.lock();
        try {
            System.out.println("线程名" + thread.getName() + "获得了锁");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("线程名" + thread.getName() + "释放量锁");
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        lockMethod lockMethod = new lockMethod();

        //线程1
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                lockMethod.method(Thread.currentThread());
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                lockMethod.method(Thread.currentThread());
            }
        }, "t2");

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

    }

}

执行结果:

tryLock() & tryLock(long time, TimeUnit unit) 顾明思议,就是尝试获取锁,可以加时间,tryLock(long time, TimeUnit unit)  能够响应中断.及支持对获取的中断,但是但尝试获取一个内部锁(synchronized)的操作是不能被中断,返回的是boolean类型这里可以进行判断,如果获取到了锁,则进行啥,若没有获取到锁执行啥,

测试代码:


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

    private Lock lock = new ReentrantLock();
    /**
     *     true 表示 ReentrantLock 的公平锁
     */
    private ReentrantLock lock = new ReentrantLock(true);

    public static void main(String[] args) {
        //简单的使用
        test1();
        //trylock
        test2();
        //中断锁
        test3();
    }

    public static void test1() {
        LockTest lockTest = new LockTest();
        for (int i = 0; i < 10; i++) {
            //线程1
            new Thread(() -> {
                lockTest.method1(Thread.currentThread());
            }, i + "").start();
        }
    }

    //需要参与同步的方法
    private void method1(Thread thread) {
        //错误
//        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            System.out.println("线程名" + thread.getName() + "获得了锁");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("线程名" + thread.getName() + "释放了锁");
            lock.unlock();
        }
    }

    public static void test2() {
        LockTest lockTest = new LockTest();
        for (int i = 0; i < 10; i++) {
            //线程1
            new Thread(() -> {
                lockTest.method2(Thread.currentThread());
            }, i + "").start();
        }
    }

    //需要参与同步的方法
    private void method2(Thread thread) {

//        尝试获取锁
        boolean b = lock.tryLock();

        //        尝试获取锁时间
//        boolean b = false;
//        try {
//            System.out.println("线程名" + thread.getName() + "尝试获取锁--");
//            b = lock.tryLock(2, TimeUnit.SECONDS);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        if (!b) {
            //未获取到锁
            System.out.println("线程名" + thread.getName() + "没有获取到锁,直接返回--");
            return;
        }
        //获取到锁
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("线程名" + thread.getName() + "获得了锁");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("线程名" + thread.getName() + "释放了锁");
            lock.unlock();
        }
    }

    public static void test3() {
        LockTest lockTest = new LockTest();
        //线程1
        Thread t1 = new Thread(() -> {
            try {
                lockTest.method3(Thread.currentThread());
            } catch (InterruptedException e) {
                System.out.println("线程名t1中断-----");
//                e.printStackTrace();
            }
        }, "t1");
        t1.start();
//线程1
        Thread t2 = new Thread(() -> {
            try {
                lockTest.method3(Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2");
        t2.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("线程名t1执行中断");
        t1.interrupt();
    }

    //需要参与同步的方法
    private void method3(Thread thread) throws InterruptedException {
        lock.lockInterruptibly();
        //获取到锁
        System.out.println("线程名" + thread.getName() + "获得了锁");
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (Exception e) {
//            e.printStackTrace();
        } finally {
            System.out.println("线程名" + thread.getName() + "释放了锁");
            lock.unlock();
        }
    }

}

默认的ReentrantLock是不公平的锁

公平所,公平锁指的是谁先尝试获取锁,谁就能获取到锁,就是有可能的一个线程最先尝试获取锁,但是一直没有获取到锁,索取锁就是随机的,可以通过构造方法创建公平锁

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

非公平锁性能高于公平锁性能,首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。而且,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。

ReadWriteLock(读写锁)

读写锁,跟lock接口没啥关系主要实现有ReentrantReadWriteLock

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

public class ReadWriteLockTest {

    private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    private static Integer count = 0;

    public static void main(String[] args) {
        ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
        for (int i = 0; i < 100; i++) {
            //从1到10的int型随数
            int j = (int) (1 + Math.random() * (10 - 1 + 1));
            if (j % 4 != 1) {
                //写操作
                new Thread("" + i) {
                    public void run() {
                        readWriteLockTest.get(Thread.currentThread());
                    }
                }.start();
            } else {
                //读操作
                int co = i;
                new Thread("" + i) {
                    public void run() {
                        readWriteLockTest.write(Thread.currentThread(), co);
                    }
                }.start();
            }
        }

    }

    public static void get(Thread thread) {
        rwl.readLock().lock();
        try {
            System.out.println("线程" + thread.getName() + "开始读操作...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + thread.getName() + "读操作完毕..." + count);
        } finally {
            rwl.readLock().unlock();
        }
    }

    public static void write(Thread thread, int i) {
        rwl.writeLock().lock();
        try {
            System.out.println("线程" + thread.getName() + "开始写操作---------"+i);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count = i;
            System.out.println("线程" + thread.getName() + "开始写操作完成------"+i);
        } finally {
            rwl.writeLock().unlock();
        }
    }


}

执行结果

å¨è¿éæå¥å¾çæè¿°

开始读写操作,线程可以都进行读操作,要是有写操作,会等读操作都完成

Lock和synchronized的选择:

总的来说,lock和synchronized的选择有以下不同点:
    1.lock是一个接口,是jdk层面的实现,而synchronized是java中的关键字,是java的内置特性,是jvm层面的实现
    2.synchronized在发生异常的时,会自动释放线程占的锁,因此不会导致死锁现象发生,而lock在发生异常时候,如果没有
    unlock()去释放锁,则很可能造成死锁现象,因此使用lock时需要在finally快中释放锁
    3.lock可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能响应中断
    4.通过lock可以知道有没有成功获取锁,而synchronized却无法办到
    5.lock可以提高多个线程进行读操作的效率
    
在性能上说,如果竞争资源 不激烈,两者性能差不多,而当竞争资源非常激烈时候(既有大量线程同事竞争) 此时lock的性能要
远远优于synchronized

猜你喜欢

转载自blog.csdn.net/CoreyXuu/article/details/89101392
今日推荐