synchronized和lock比对

前言:在上面的博客说了synchronized的一些用法,下面咱们再来看看lock,这个出现频率也是很是高的一个。html

1:获取Lock锁的几种方式

前面说了synchronized有锁对象和锁类对象,当某个线程获取锁其余线程必须等待执行完毕才可继续进行,好比线程A先获取锁,可是出现异常致使的后果就是线程B没法获取锁,会出现死锁的状况(http://www.cnblogs.com/LipeiNet/p/6475851.html),那么咱们一块儿看看Lock是如何解决的。lock有4种方式来获取锁缓存

1:lock.lock() 若是获取了锁当即返回,若是别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁。此种模式和synchronized同样可是不会出现死锁并发

public class Lock1 {
    static int value = 0;
    static Lock lock = new ReentrantLock();

    static class Task1 implements Runnable {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
            lock.lock();
            try {
                for (int i = 0; i < 1000000; i++) {
                    value++;
                }
                System.out.println(value);
            } finally {
                lock.unlock();
            }
        }
    }
    static class Task2 implements Runnable {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
            lock.lock();
            try {
                for (int i = 0; i < 1000000; i++) {
                    value++;
                }
                System.out.println(value);
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService service= Executors.newCachedThreadPool();
        service.execute(new Task1());
        service.execute(new Task2());
        service.shutdown();
    }
}

输出结果很明显其中一个value是1000000,一个是2000000,效果和synchronized是同样的。可是若是咱们去掉lock之后的结果呢,很明显会错,以下图这样this

为啥会出现这样状况呢,是因为cpu速度极快,每次处理完毕以后并无当即把数值放入Java内存中,而是放在写缓存区,而后由写缓存区同步到Java内存中,这样同样,若是线程1计算结果是2,可是仍是到内存中,致使线程2觉得value值仍是1因此会重复计算,还有从结果咱们也能够看出value值并非100000说明2个线程是同步执行的。spa

 2:lock.tryLock();线程

这个方法和synchronized有所不一样,synchronized和lock都会等待直到获取锁。若是获取了锁当即返回true,若是别的线程正持有锁,当即返回false;固然咱们能够利用while循环一直等待,直到获取锁而后进行。代码以下3d

public class Lock2 {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                String tName = Thread.currentThread().getName();
                while (!lock.tryLock()) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("等待获取锁");
                }
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                String tName = Thread.currentThread().getName();
                while (!lock.tryLock()) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("等待获取锁");
                }
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }

                } catch (Exception e) {
                    System.out.println(tName + "出错了!!!");
                } finally {
                    System.out.println(tName + "释放锁!!");
                    lock.unlock();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

3:lock.trylock(long time, TimeUnit unit)若是获取了锁定当即返回true,若是别的线程正持有锁,会等待参数给定的时间,在等待的过程当中,若是获取了锁定,就返回true,若是等待超时,返回false;unit是time的时间单位好比TimeUnit.SECONDS就是表示秒code

4:lock.lockInterruptibly()若是获取了锁定当即返回,若是没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断。也就是说如何线程没有被中断和lock.lock()的做用同样。可是如何线程被中断了,那么此时这个线程不会有任何的响应,想象这么一个场景,线程A和线程B同时执行任务,可是必须等待线程B先执行,可是执行过程当中忽然线程A忽然被中断,那么这个时候就可能出现死锁,哪怕是在finally中加入unlock,这个时候咱们就要采用lockInterruptibly()了。代码以下htm

public class Lock3 {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        final Thread thread1 = new Thread(new Runnable() {
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("等待被中断");
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("我被中断了");
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                lock.lock();
                try {
                    TimeUnit.SECONDS.sleep(4);
                } catch (InterruptedException e) {

                }
                thread1.interrupt();
                System.out.println("线程1已经被中断");
            }
        });
        thread1.start();
        thread2.start();
    }
}

2:读锁和写锁

 在开发中咱们最好的愿望就是写的时候加锁,可是读的时候不加锁这样会大大的提高效率,可是采用synchronized却没法知足咱们的要求,若是在读的方法面前加锁那么全部的读都须要等待,若是不加锁的话那么若是如今A,B2个线程读取,C线程写入可能致使的后果就是A,B2个线程取得数据不一致,明明同一种业务场景可是获取值却不一样。好了lock的读锁和写锁帮助咱们实现这种功能。对象

public class ReadWriteLockTest {
    private int value = 0;
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void add(int value) {
        Lock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("添加开始时间:" + new Date());
            this.value += value;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }

    public void getValue() {
        Lock readLock = readWriteLock.readLock();
        readLock.lock();
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("获取开始时间:" + new Date());
            System.out.println(value);
        } catch (InterruptedException e) {

        } finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) {
        final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
        Runnable task1 = new Runnable() {
            public void run() {
                readWriteLockTest.add(100);
            }
        };
        Runnable task2 = new Runnable() {
            public void run() {
                readWriteLockTest.getValue();
            }
        };
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i=0;i<2;i++){
            service.execute(task1);
        }
        for (int i=0;i<2;i++){
            service.execute(task2);
        }
        for (int i=0;i<2;i++){
            service.execute(task1);
        }
        service.shutdown();
    }
}

运行结果:

从这个结果咱们很明显的能够总结读锁和写锁

第一:若是执行写的时候,读和写必须等待

第二:若是执行读的时候,写必须等待,而读却不用等待

也就是说读和写必须存在前后顺序,不论是先读仍是先写。

3:总结

相同点:lock能实现synchronized全部能够实现的

不一样点:

1:lock不容易出现死锁,而synchronized若是某个线程出现异常就会产生死锁

2:lock更加灵活,能够经过tryLock来验证是否获取锁,在线程中断也一样能够处理

3:lock有读写锁在并发量大的时候具备很大的优点,由于读的状况通常会比写多不少

相关文章
相关标签/搜索