在java中有两种方法实现锁机制,一种是线程同步机制:synchronized,而另一种是比synchronized更加强大和领过的Lock。Lock确保当一个线程位于代码的临界区时,另一个线程不进入临界区,相对于synchronized,Lock接口及其实现类提供了更加强大、灵活的锁机制。

自Java 5开始,java.util.concurrent.locks包中包含了一些锁的实现,因此你不用去实现自己的锁了。但是你仍然需要去了解怎样使用这些锁,且了解这些实现背后的理论也是很有用处的

一个简单的锁

public class Test{private int count = 0;public int inc(){synchronized(this){return ++count;}}
}

同一个对象,在多个线程中,只有一个线程更新,得到同步的机会哦。

我们自己写一个类似的锁机制,替换掉系统给我们实现的吧

package ThredDemo;public class Lock {private boolean isLock=false;public synchronized void Lock() throws InterruptedException{while(isLock){wait();}isLock=true;}public synchronized void unlock(){isLock=false;notify();}
}

当isLocked为true时,调用lock()的线程在wait()调用上阻塞等待。为防止该线程没有收到notify()调用也从wait()中返回(也称作虚假唤醒),这个线程会重新去检查isLocked条件以决定当前是否可以安全地继续执行还是需要重新保持等待,而不是认为线程被唤醒了就可以安全地继续执行了。如果isLocked为false,当前线程会退出while(isLocked)循环,并将isLocked设回true,让其它正在调用lock()方法的线程能够在Lock实例上加锁。
当线程完成了临界区(位于lock()和unlock()之间)中的代码,就会调用unlock()。执行unlock()会重新将isLocked设置为false,并且通知(唤醒)其中一个(若有的话)在lock()方法中调用了wait()函数而处于等待状态的线程。

下面是我们的测试例子:

package ThredDemo;
/*** * @author JetWang**/
public class ThreadLock {private int count = 0;Lock l = new Lock();public void test() {// TODO Auto-generated method stubtry {l.Lock();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + "count:"+ count);}l.unlock();}public static void main(String[] args) {ThreadLock testLock = new ThreadLock();for (int i = 0; i < 3; i++) {new Thread(new Runnable() {@Overridepublic void run() {testLock.test();}}).start();}}
}

Thread-0count:1
Thread-0count:2
Thread-0count:3
Thread-0count:4
Thread-0count:5
Thread-1count:6
Thread-1count:7
Thread-1count:8
Thread-1count:9
Thread-1count:10
Thread-2count:11
Thread-2count:12
Thread-2count:13
Thread-2count:14
Thread-2count:15
这里为了保证,操作的是同一个对象,所以。我们的Lock放在属性中,而不是在Test中生成的,不然每一个线程中都有一个lock对象,在我们自己写的Lock中就不会同步,以为没有唯一的区分,不同的lock对象,同步方法块肯定就是不行的啦!好好的理解,不懂的看哈上面篇

锁的公平性:

公平性的对立面是饥饿。那么什么是“饥饿”呢?如果一个线程因为其他线程在一直抢占着CPU而得不到CPU运行时间,那么我们就称该线程被“饥饿致死”。而解决饥饿的方案则被称之为“公平性”——所有线程均可以公平地获得CPU运行机会。

导致线程饥饿主要有如下几个原因:操作系统说的就是调度的策略问题
高优先级线程吞噬所有的低优先级线程的CPU时间。我们可以为每个线程单独设置其优先级,从1到10。优先级越高的线程获得CPU的时间越多。对大多数应用来说,我们最好是不要改变其优先级值。

线程被永久堵塞在一个等待进入同步块的状态。java的同步代码区是导致线程饥饿的重要因素。java的同步代码块并不会保证进入它的线程的先后顺序。这就意味着理论上存在一个或者多个线程在试图进入同步代码区时永远被堵塞着,因为其他线程总是不断优于他获得访问权,导致它一直得到不到CPU运行机会被“饥饿致死”。

线程在等待一个本身也处于永久等待完成的对象。如果多个线程处在wait()方法执行上,而对其调用notify()不会保证哪一个线程会获得唤醒,任何线程都有可能处于继续等待的状态。因此存在这样一个风险:一个等待线程从来得不到唤醒,因为其他等待线程总是能被获得唤醒。
为了解决线程“饥饿”的问题,我们可以使用锁实现公平性。本文所展现的锁在内部是用synchronized同步块实现的,因此它们也不保证公平性。

锁的可重入性

Java中的synchronized同步块是可重入的。这意味着如果一个java线程进入了代码中的synchronized同步块,并因此获得了该同步块使用的同步对象对应的管程上的锁,那么这个线程可以进入由同一个管程对象所同步的另一个java代码块。获得了对象锁之后,我们可以再次获得相同的涩

Reentrant 再进去的,凹角的
网络 可重入; 可重入的; 重入

public class Reentrant{public synchronized outer(){inner();}public synchronized inner(){//do something}
}

java多线程的可重入性的实现是通过每个锁关联一个请求计算和一个占有它的线程,当计数为0时,认为该锁是没有被占有的,那么任何线程都可以获得该锁的占有权。当某一个线程请求成功后,JVM会记录该锁的持有线程 并且将计数设置为1,如果这时其他线程请求该锁时则必须等待。当该线程再次请求请求获得锁时,计数会+1;当占有线程退出同步代码块时,计数就会-1,直到为0时,释放该锁。这时其他线程才有机会获得该锁的占有权。

public class Father {public synchronized void method(){//do something}
}
public class Child extends Father{public synchronized void method(){//do something super.method();}
}

如果所是不可重入的,上面的代码就会死锁,因为调用child的method(),首先会获取父类Father的内置锁然后获取Child的内置锁,当调用父类的方法时,需要再次后去父类的内置锁,如果不可重入,可能会陷入死锁。

??怎么修改刚刚我们的那个锁呢

第一要记住锁住的哪个?锁的次数?
下面的这个我们如果不修改上面的哪个Lock函数肯定不行的,为啥呢?我们的那个while循环体,没有考虑可重入性的问题。所以需要修改啦!

两次lock()之间没有调用unlock(),第二次调用lock就会阻塞,看过lock()实现后,

public class Reentrant2{Lock lock = new Lock();public outer(){lock.lock();inner();lock.unlock();}public synchronized inner(){lock.lock();//do somethinglock.unlock();}
}

一个线程是否被允许退出lock()方法是由while循环(自旋锁)中的条件决定的。当前的判断条件是只有当isLocked为false时lock操作才被允许,而没有考虑是哪个线程锁住了它。

可重入锁LOCK

package ThredDemo;
/*** 可重入的锁* @author JetWang**/
public class Lock {private boolean isLock=false;private Thread isLocakBy=null;//哪个线程锁住了private int count=0;//数量是什么?public synchronized void Lock() throws InterruptedException{Thread thread=Thread.currentThread();while(isLock&&thread==isLocakBy){//满足这两个条件才阻塞wait();}isLock=true;}public synchronized void unlock(){if(Thread.currentThread()==isLocakBy){count--;}if(count==0){isLock=false;notify();}}
}

在finally语句中调用unlock()
如果用Lock来保护临界区,并且临界区有可能会抛出异常,那么在finally语句中调用unlock()就显得非常重要了。这样可以保证这个锁对象可以被解锁以便其它线程能继续对其加锁。

lock.lock();
try{//do critical section code,//which may throw exception
} finally {lock.unlock();
}

java锁以及实现类图:
java.util.concurrent.locks提供了非常灵活锁机制,为锁定和等待条件提供一个框架的接口和类,它不同于内置同步和监视器,该框架允许更灵活地使用锁定和条件。它的类结构图如下:

慢慢的了解这些东西

码农小汪-锁 LOCK相关推荐

  1. 码农小汪-设计模式之-Builder模式

    建造者模式 将一个复杂的对象的构建与它的表示分离,使得同样构建的过程中可以创建不同的表示.这个话语看起来,好累啊!真心很难理解. 下面是它的UML图: 抽象建造者角色(Builder):为创建一个Pr ...

  2. 码农小汪-Hibernate学习8-hibernate关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable...

    近期我也是有点郁闷,究竟是程序中处理关联关系.还是直接使用外键处理关联关系呢?这个的说法不一致!程序中处理这样的关联关系的话.自己去维护这样的约束.这样的非常乐观的一种做法!或者是直接在数据库中处理这 ...

  3. 码农小汪-Volatile和Transient

    Volatile: Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看 ...

  4. 码农小汪-设计模式之-命令模式

    大话设计模式的例子讲的非常的好,理解起来也方便!有时候忘了.想到这些特殊的例子感觉就是特别爽. 烤羊肉串带来的思考! 路边摊羊肉串: 老板,我这里排的比较先啊,我最先给钱.老板这个没有熟啊.我的是 辣 ...

  5. 码农翻身 各章节链接

    大话编程 我是一个线程 我是一个Java class Javascript: 一个屌丝的逆袭 Java:一个帝国的诞生 JSP:一个装配工的没落 TCP/IP 之 大明王朝的邮差 TCP/IP 之 大 ...

  6. 码农翻身全年文章精华

    在码农翻身公众号写了一年多, 最大的体会就是:原创真心不易! 每天思考的最大问题就是: 下一篇文章写啥? 在大家的支持和鼓励下,还是坚持了下来,  回头看看走过的路,这一年过得还算充实. 很快就要过年 ...

  7. 码农翻身全年文章精华2016

    在码农翻身公众号写了一年多, 最大的体会就是:原创真心不易! 每天思考的最大问题就是: 下一篇文章写啥? 在大家的支持和鼓励下,还是坚持了下来,  回头看看走过的路,这一年过得还算充实. 很快就要过年 ...

  8. 德国码农开发抢厕纸神器,再也不用愁了

    ,点击 欧盟IT那些事 关注我们 公告:因企鹅审核规定,本公众号从<德国IT那些事>更名为<欧盟IT那些事>. 厕纸在手,天下我有! 疫情中由于欧美人民对厕纸的蜜汁喜爱,导致超 ...

  9. 身价过亿的冰山御萝对小码农说你液晶能显示吗

    文章目录 小码农为了御萝,咳咳,为了知识怎么敢说液晶不显示啊 ==联动文章 [身价过亿的冰山御萝的姐姐对小码农说你有大一点的液晶吗](https://blog.csdn.net/diandengren ...

  10. 身价过亿的妖媚子对小码农说串口能传送我的爱吗?

    文章目录 小码农不敢接受妖媚子(想多活几年),但是串口还是可以传送数据的 并行通信 串行通信 ==同步:== ==异步:== 串口通信:收发一个字节(只能用时间来同步) 串口通讯的相关术语 STC串口 ...

最新文章

  1. Oracle中的NULL(二、NULL详解)
  2. PythonNET网络编程3
  3. 【ABAP增强】基于BADI的增强
  4. Matlab控制精度
  5. Docker中安装Jenkins实时发布.net core 项目(一)
  6. 4.3.2 用jQuery进行异步加载(2)
  7. python上下文管理器细读
  8. php xml 接口调用,php的SimpleXML方法读写XML接口文件实例解析
  9. linux mysql emoji_Linux宝塔面板MySQL存储emoji,非服务器命令方法
  10. AttributeError: module 'tensorflow' has no attribute 'python'
  11. java 调用gpu_GPU使用设置
  12. 部分iPhone13 系统有bug
  13. echarts年龄饼图_echarts自定义饼图
  14. codeigniter_MY_Model
  15. 数据的提取方法 - 1
  16. MySQL 数据库崩溃(crash)的常见原因和解决办法---发表到 《数据和云》 公众号
  17. WEB开发常用软件集合
  18. oracle库客户端完整卸载,卸载Oracle数据库或客户端​
  19. java下拉刷新上拉加载_使用PullToRefresh实现下拉刷新和上拉加载
  20. multisim变压器反馈式_基于Multisim 负反馈放大电路的仿真实验分析

热门文章

  1. 旧版本ios软件整理
  2. java数据类型简介
  3. 数据库架构设计——索引结构设计
  4. 新联想ISG聚焦新IT,全要素推进企业智能化转型
  5. 基于springboot高校社团管理系统
  6. 产品需求文档模板,不用找了(附“简”例)【转】
  7. 官网jdk8,jdk11下载时需要登录Oracle账号的问题解决
  8. JAVA 身份证号码的验证
  9. Microsoft Word 2010 - 符号 / 特殊符号
  10. 华为路由器OSPF+RIP+静态路由配置实例