java 线程安全 Lock-阿里云开发者社区

开发者社区> 技术小甜> 正文

java 线程安全 Lock

简介:
+关注继续查看

对于线程安全我们前面使用了synchronized关键字,对于线程的协作我们使用Object.wait()和Object.notify()。在JDK1.5中java为我们提供了Lock来实现与它们相同的功能,并且性能优于它们,在JDK1.6时,JDK对synchronized做了优化,在性能上两种方式差距不大了。

一、为什么出现lock

  synchronized修饰的代码块,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,如果没有释放则需要无限的等待下去。获取锁的线程释放锁只会有两种情况:

  1、获取锁的线程执行完了该代码块,然后线程释放对锁的占有。

  2、线程执行发生异常,此时JVM会让线程自动释放锁。

Lock与synchronized对比:

  1、Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问。

  2、synchronized不需要手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

二、java.util.concurrent.locks包中常用的类和接口。

public interface Lock {    //用来获取锁。如果锁已被其他线程获取,则进行等待。
    void lock();   // 当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态
    void lockInterruptibly() throws InterruptedException;    //它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false
    boolean tryLock();    //与tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    //释放锁
    void unlock();
    Condition newCondition();
}

1、Lock与unlock
Lock用于获取锁,但它不会主动释放锁所以需要与unlock()配合使用。一般在使用Lock时必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

package com.jalja.base.threadTest;import java.util.concurrent.locks.ReentrantLock;public class LockTest implements Runnable{    public static ReentrantLock lock=new ReentrantLock();    public static int c=0;    public void run() {        for(int i=0;i<1000;i++){
            lock.lock();//获取锁
            try {
                System.out.println(Thread.currentThread().getName()+"获得锁");
                System.out.println(Thread.currentThread().getName()+"====>"+c);
                c++;
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                System.out.println(Thread.currentThread().getName()+"释放锁");
                lock.unlock();//释放锁            }
        }
    }    public static void main(String[] args) {
        LockTest lt=new LockTest();
        Thread thread1=new Thread(lt);
        Thread thread2=new Thread(lt);
        thread1.start();
        thread2.start();        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(c);
    }
}

注意:同一个线程可以连续获得同一把锁,但也必须释放相同次数的锁。允许下面的写法

          lock.lock();//获取锁            lock.lock();
            lock.lock();            try {
                System.out.println(Thread.currentThread().getName()+"获得锁");
                System.out.println(Thread.currentThread().getName()+"====>"+c);
                c++;
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                System.out.println(Thread.currentThread().getName()+"释放锁");
                lock.unlock();//释放锁
                lock.unlock();//释放锁
                lock.unlock();//释放锁
            }

2、获取锁等待时间tryLock(long time, TimeUnit unit)
如果你约朋友打篮球,约定时间到了你朋友还没有出现,你等1小时后还是没到,我想你肯定会扫兴的离去。对于线程来说也应该时这样的,因为通常我们是无法判断一个线程为什么会无法获得锁,但我们可以给该线程一个获取锁的时间限制,如果到时间还没有获取到锁,则放弃获取锁。

package com.jalja.base.threadTest;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class TryLockTest implements Runnable{    public static ReentrantLock lock=new ReentrantLock();    private static int m=0;    public void run() {        try {            if(lock.tryLock(1, TimeUnit.SECONDS)){//设置获取锁的等待时长1秒
                System.out.println(Thread.currentThread().getName()+"获得锁");
                m++;                //Thread.sleep(2000);//设置休眠2秒
            }else{
                System.out.println(Thread.currentThread().getName()+"未获得锁");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }    public static void main(String[] args) {
        TryLockTest thread1=new TryLockTest();
        TryLockTest thread2=new TryLockTest();
        Thread th1=new Thread(thread1);
        Thread th2=new Thread(thread2);
        th1.start();
        th2.start();        try {            //让main线程等待th1、th2线程执行完毕后,再继续执行            th1.join();
            th2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m);
    }
}

执行结果:

Thread-0获得锁
Thread-1获得锁2

  该代码就是让线程在锁请求中,最多等待1秒,如果超过一秒没有获得锁就返回false,如果获得了锁就返回true,根据执行结果可以看出Thread-1线程在1秒内获得了锁。

我们开启注释 //Thread.sleep(2000);就会发现Thread-1或Thread-0一定会有一个是未获得锁,这是因为占用锁的线程时间是2秒,而等待锁的线程等待时间是1秒,所以在1秒后的瞬间它就放弃了请求锁操作。


















本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1950731 ,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock。 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍ReentrantLock。
1073 0
Centos7下Docker上部署webgoat(用来演示Web应用程序中的典型安全漏洞的应用程序)
Centos7下Docker上部署webgoat(用来演示Web应用程序中的典型安全漏洞的应用程序)
654 0
《Java安全编码标准》一1.2 注入攻击
本节书摘来自华章出版社《Java安全编码标准》一书中的第1章,第1.2节,作者 (美)Fred Long,Dhruv Mohindra,Robert C. Seacord,Dean F. Sutherland,David Svoboda,更多章节内容可以访问云栖社区“华章计算机”公众号查看
1326 0
Servlet中的线程安全
        Servlet 之所有效率比CGI高,是因为CGI每一个请求都会FORK出一个进程来处理相应的请求,而Servlet则是用线程来处理的,但随之而来就有一个问题:多线程的安全问题!       在Servlet中,类的成员变量是共享的,所有的请求都共用成员变量,为了保证线程安全,我们应该尽量做到如下几点:      1.用方法变量,即局部变量来保存请求中的专有数据,因为每个线程都会有自己的一份局部变量,这样就不会相互影响了。
731 0
多线程编程学习四(Lock 的使用)
一、前言     本文要介绍使用Java5中 Lock 对象,同样也能实现同步的效果,而且在使用上更加方便、灵活,主要包括 ReentrantLock 类的使用和ReentrantReadWriteLock 类的使用。
813 0
ArrayList为什么线程不安全 线程不安全体现在哪些方面 源码角度分析其具体原因
我们都知道ArrayList是线程不安全的,那么它不安全在哪里?又会出现什么并发问题呢?
251 0
线程安全
什么是线程安全,个人在线程中使用共享资源时,能够保证共享资源在任何时候都是原子的、一致的,这样的线程就是线程安全的线程 首先来介绍一下共享资源的类型(这是我自己分类的,为了后文好解释),共享资源从其类型可以分为三类(下文讲到变量一律指类变量或实例变量,不再特别指出): 1.
861 0
+关注
10146
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载