JAVA基础知识|synchronized和lock

一、synchronized

是jvm的一个关键字,使用过程均由jvm控制

有三种使用方式:

  • 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰代码块,同方法
  • 修饰静态方法,作用于当前加锁,进入同步代码前要获得当前类对象的锁

1.1、实例方法

作用于实例,会阻塞其他线程访问本实例,但不会影响其他线程访问其他实例

如果实例中有多个synchronized修饰的方法,当其中一个方法被访问时,其他方法也不可以被访问,所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步

【示例】

import java.util.concurrent.TimeUnit;

/**
 * Created by jyy on 2018/6/21.
 */
public class SyncTest {

    public synchronized void test1(){
        System.out.println("test1");
        try {
            TimeUnit.SECONDS.sleep(10);
        }catch(InterruptedException ie){
            System.out.println("中断异常");
        }
        System.out.println("test1");
    }

    public synchronized void test2(){
        System.out.println("test2");
    }

    public void test3(){
        System.out.println("test3");
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by jyy on 2018/4/19.
 */
public class Test {

    public static void main(String[] args){
        //需要声明为final类型
        final SyncTest syncTest = new SyncTest();
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test2();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test3();
            }
        });
    }
}

执行结果:

test1
test3
test1
test2

1.2、代码块

作用于实例,同上

【示例】

    //新增三个方法
    public void test4(){
        System.out.println("test4");
        synchronized(this){
            try {
                TimeUnit.SECONDS.sleep(10);
            }catch(InterruptedException ie){
                System.out.println("中断异常");
            }
            System.out.println("test4");
        }
    }

    public void test5(){
        System.out.println("test5");
        synchronized(this){
            System.out.println("test5");
        }
    }

    public void test6(){
        System.out.println("test6");
    }
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by jyy on 2018/4/19.
 */
public class Test {

    public static void main(String[] args){
        //需要声明为final类型
        final SyncTest syncTest = new SyncTest();
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test4();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test5();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                syncTest.test6();
            }
        });
    }
}

执行结果:

test4
test5
test6
test4
test5

从执行结果上,可以看出,当访问关键字synchronized修饰的代码块时,其他synchronized修饰的代码块也会被锁住

1.3、静态方法

作用于类,会阻塞其他线程访问本类的同步方法,即使是不同的实例也不行

【示例】

    //新增方法
    public synchronized static void test7(){
        int random= (new Random()).nextInt(100);
        System.out.println(random);
        try {
            TimeUnit.SECONDS.sleep(10);
        }catch(InterruptedException ie){
            System.out.println("中断异常");
        }
        System.out.println(random);
    }
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by jyy on 2018/4/19.
 */
public class Test {

    public static void main(String[] args){
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                SyncTest.test7();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                SyncTest.test7();
            }
        });
    }
}

执行结果:

69
69
67
67

二、lock

Lock是一个接口,其中lock()、lockInterruptibly()、tryLock()、tryLock(long time, TimeUnit unit)方法可以获取锁,unlock()用来释放锁

 

如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

2.1、ReentrantLock

ReentrantLock是实现了Lock接口的类,通过它,我们来看下四种获取锁方法的区别

2.1.1、lock()

【示例1】

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

/**
 * Created by jyy on 2018/6/25.
 */
public class ReentrantLockTest {
    private Lock lock = new ReentrantLock();//需要声明为全局变量

    public void test1(){
        lock.lock();//获取锁
        try {
            System.out.println(Thread.currentThread()+"得到了锁");
            TimeUnit.SECONDS.sleep(5);
        } catch (Exception e) {
            System.out.println("发生异常");
        }finally {
            System.out.println(Thread.currentThread()+"释放了锁");
            lock.unlock();
        }
    }
}
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        final ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest.test1();
            }
        });
       executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest.test1();
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了锁
Thread[pool-1-thread-1,5,main]释放了锁
Thread[pool-1-thread-2,5,main]得到了锁
Thread[pool-1-thread-2,5,main]释放了锁

【示例2】

        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        final ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest.test1();
            }
        });
        final ReentrantLockTest reentrantLockTest1 = new ReentrantLockTest();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest1.test1();
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了锁
Thread[pool-1-thread-2,5,main]得到了锁
Thread[pool-1-thread-1,5,main]释放了锁
Thread[pool-1-thread-2,5,main]释放了锁

示例2说明Lock锁住的是实例,而不是类

2.1.2、tryLock()

tryLock()尝试获取锁,获取不到返回false,不影响代码继续执行

【示例】

    //新增方法
    public void test2(){
        if(lock.tryLock())//获取锁
        {
            try {
                System.out.println(Thread.currentThread() + "得到了锁");
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                System.out.println("发生异常");
            } finally {
                System.out.println(Thread.currentThread() + "释放了锁");
                lock.unlock();
            }
        }else{
            System.out.println(Thread.currentThread() + "获取锁失败");
        }
    }
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        final ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest.test2();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantLockTest.test2();
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了锁
Thread[pool-1-thread-2,5,main]获取锁失败
Thread[pool-1-thread-1,5,main]释放了锁

2.1.3、tryLock(long time, TimeUnit unit)

【示例】

    //新增方法
    public void test3() throws InterruptedException {
        if(lock.tryLock(10,TimeUnit.SECONDS))//获取锁
        {
            try {
                System.out.println(Thread.currentThread() + "得到了锁");
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                System.out.println("发生异常");
            } finally {
                System.out.println(Thread.currentThread() + "释放了锁");
                lock.unlock();
            }
        }else{
            System.out.println(Thread.currentThread() + "获取锁失败");
        }
    }
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        final ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    reentrantLockTest.test3();
                }catch(InterruptedException ie){
                    System.out.println("发生异常");
                }
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    reentrantLockTest.test3();
                }catch(InterruptedException ie){
                    System.out.println("发生异常");
                }
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了锁
Thread[pool-1-thread-1,5,main]释放了锁
Thread[pool-1-thread-2,5,main]得到了锁
Thread[pool-1-thread-2,5,main]释放了锁

尝试10s获取锁,获取不到才会返回false

2.1.4、lockInterruptibly()

【示例】

    //新增方法
    public void test4() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            System.out.println(Thread.currentThread()+"得到了锁");
            TimeUnit.SECONDS.sleep(10);
        } catch (Exception e) {
            System.out.println("中断成功");
        }finally {
            System.out.println(Thread.currentThread()+"释放了锁");
            lock.unlock();
        }
    }
        final ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    reentrantLockTest.test4();
                }catch(InterruptedException ie){
                    System.out.println("发生异常");
                }
            }
        },"1");
        thread1.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (Exception e) {
            System.out.println("延迟5s");
        }
        System.out.println("开始中断");
        thread1.interrupt();//中断thread1
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    reentrantLockTest.test4();
                }catch(InterruptedException ie){
                    System.out.println("发生异常");
                }
            }
        },"2");
        thread2.start();

执行结果:

Thread[1,5,main]得到了锁
开始中断
中断成功
Thread[1,5,main]释放了锁
Thread[2,5,main]得到了锁
Thread[2,5,main]释放了锁

成功中断thread1的锁

三、读写锁

3.1、ReadWriteLock

ReadWriteLock是一个接口,只包含两个方法readLock()、writeLock()

3.2、ReentrantReadWriteLock

ReentrantReadWriteLock实现了ReadWriteLock接口,下面主要说一下ReadLock()、WriteLock()两个方法

 【示例1】

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by jyy on 2018/6/26.
 */
public class ReentrantReadWriteLockTest {

    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    /**
     * 读锁
     */
    public void test1(){
        rwl.readLock().lock();
        try {
            System.out.println(Thread.currentThread() + "得到了读锁");
            TimeUnit.SECONDS.sleep(5);
        } catch (Exception e) {
            System.out.println("发生异常");
        } finally {
            System.out.println(Thread.currentThread() + "释放了读锁");
            rwl.readLock().unlock();
        }
    }

    /**
     * 写锁
     */
    public void test2(){
        rwl.writeLock().lock();
        try {
            System.out.println(Thread.currentThread() + "得到了写锁");
            TimeUnit.SECONDS.sleep(5);
        } catch (Exception e) {
            System.out.println("发生异常");
        } finally {
            System.out.println(Thread.currentThread() + "释放了写锁");
            rwl.writeLock().unlock();
        }
    }

}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by jyy on 2018/4/19.
 */
public class Test {

    public static void main(String[] args) throws InterruptedException {
        final ReentrantReadWriteLockTest reentrantReadWriteLockTest = new ReentrantReadWriteLockTest();
        ExecutorService executorService = Executors.newCachedThreadPool();//连接池
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
    }
}

执行结果:

Thread[pool-1-thread-1,5,main]得到了读锁
Thread[pool-1-thread-2,5,main]得到了读锁
Thread[pool-1-thread-3,5,main]得到了读锁
Thread[pool-1-thread-3,5,main]释放了读锁
Thread[pool-1-thread-1,5,main]释放了读锁
Thread[pool-1-thread-2,5,main]释放了读锁

多个线程都可以获取到读锁

【示例2】

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test2();
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了读锁
Thread[pool-1-thread-2,5,main]得到了读锁
Thread[pool-1-thread-2,5,main]释放了读锁
Thread[pool-1-thread-1,5,main]释放了读锁
Thread[pool-1-thread-3,5,main]得到了写锁
Thread[pool-1-thread-3,5,main]释放了写锁

访问写锁时,必须等读锁全部释放

【示例3】

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test2();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test1();
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                reentrantReadWriteLockTest.test2();
            }
        });

执行结果:

Thread[pool-1-thread-1,5,main]得到了写锁
Thread[pool-1-thread-1,5,main]释放了写锁
Thread[pool-1-thread-2,5,main]得到了读锁
Thread[pool-1-thread-2,5,main]释放了读锁
Thread[pool-1-thread-3,5,main]得到了写锁
Thread[pool-1-thread-3,5,main]释放了写锁

想要获取读锁,必须先等到写锁释放

总结:

1、如果一个线程占用了读锁,其他线程依然可以获取到这个读锁,但不可以获取到写锁

2、如果一个线程占用了写锁,其他线程不可以获取到读锁和写锁

四、synchronized与lock的区别

1)synchronized是java的一个关键字,而Lock是一个接口

2)synchronized发生异常,会自动释放占有的锁,而Lock必须要主动释放锁,否则会一直处于占用状态

3)Lock中的lockInterruptibly()可以响应中断,而synchronized不可以

4)Lock中的tryLock()可以尝试获取锁,判断是否成功获取到锁,而synchronized不可以

5)ReentrantReadWriteLock可以声明读写锁,提高查询效率

两者不是非一即二的存在,具体使用要看不同的场景

五、锁类型

可重入锁:在执行对象中所有同步方法不用再次获得锁

举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2

synchronized和ReentrantLock都是可重入锁

可中断锁:在等待获取锁过程中可中断,如ReentrantLock中的lockInterruptibly()

公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利

ReentrantLock默认是非公平锁,可以通过new ReentrantLock(true)声明为公平锁

读写锁:对资源读取和写入的时候拆分为两部分处理,读的时候可以多线程一起读,写的时候必须同步地写,如ReentrantReadWriteLock

猜你喜欢

转载自www.cnblogs.com/maikucha/p/9141438.html
今日推荐