首页 > 编程语言 > 详细

java-锁

时间:2019-11-18 23:22:29      阅读:81      评论:0      收藏:0      [点我收藏+]

一  java-锁 目的 

    目的:防止多线程执行某段代码时导致的数据异常,互相干扰,所以把某段代码块加上锁,保证其原子性

 

二    使用  synchronized关键字   (java语言内置锁)

    2.1 单独使用于对象,使用对象锁
object object_lock = new object();

public void run()

{

     //被锁住的代码块,保证了原子性,同一时间只能被一个线程所操作
    synchronized(object_lock){
       do something;
    }

}

Java中的每个对象都有一个监视器,来监测并发代码的重入,上面就synchronized获取了lock的监视器,

    2.2  synchronized 用于方法

public class Thread1 implements Runable{

public synchronized void run()
{
    do something
}

}

 

其实是获取了Thread1的监视器,对象锁。

线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,
其他访问该对象的线程就无法再访问该对象的其他同步方法。

 

三   java 显示锁

2.1 第一种显示锁  Lock

实现为ReentrantLock()  ,是语法层级的锁,

常用方法有

lock();//获取锁

unlock();//释放锁

trylock();//尝试获取锁

使用范式为

//显示锁
Lock lock = new ReentrantLock();
//获取锁
lock.lock();
try {
//执行业务

} finally {
//释放锁
lock.unlock();
}

获取锁之后,一定要在finally中释放掉,

Lock与synchronized的比较

1 synchronized 代码简洁
2 Lock获取锁可以被中断,可以设置超时,可以尝试获取锁

    如果业务场景满足2中这几点,可以考虑用Lock,否则就用synchronized。

 

2.2 第2种显示锁  读写锁 ReadWriteLock 接口

实现为 ReentrantReadWriteLock

    概念:同一时刻允许多个线程同时访问,但是写线程访问的时候,所有线程都被阻塞
最适用于读多写少情况,比普通线程性能几何倍增加

用法

//读写锁对象
ReadWriteLock myLock = new ReentrantReadWriteLock();
//读锁
Lock readLock = myLock.readLock();
//写锁
Lock writeLock = myLock.writeLock();


然后在多线程中,读的地方用读锁,写的地方用写锁即可。

 

2.3 Lock 里面的等待和通知

实现:

Lock lock = new ReentrantReadWriteLock();
Condition kmCondition = lock.newCondition();

kmCondition.await();//等待
kmCondition.signal();//通知

demo 等待快递地址,和公里数变化
**
* 快递信息类
*
* @author hup
* @since 2018-09-10 22:30
*/
public class Express {
/**
* 变化公里数
*/
private int km = 0;

/**
* 地址
*/
private String site = "上海";

//显示锁
private Lock lock = new ReentrantLock();

private Condition kmCondition = lock.newCondition();
private Condition siteCondition = lock.newCondition();


//公里数变化
public void changeKm() {
lock.lock();
try {
this.km = 102;
//通知
kmCondition.signal();
} finally {
lock.unlock();
}
}

//地址变化
public void changeSite() {
lock.lock();
try {
this.site = "北京";
//通知
siteCondition.signal();
} finally {
lock.unlock();
}
}


/**
* 等待公里数变化
*/
public void waitKm() {
lock.lock();
try {
while (km < 100) {
try {
//等待
kmCondition.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "公里数发生变化,现在公里数=" + km);
} finally {
lock.unlock();
}
}

/**
* 等待地址变化
*/
public void waitSite() {
lock.lock();
try {
while (site.equalsIgnoreCase("上海")) {
try {
//等待
siteCondition.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "地址发生变化,现在地址=" + site);
} finally {
lock.unlock();
}
}
}

 

/**
* 测试类
* @author hup
* @since 2018-09-20 22:40
*/
public class mainTest {

//快递信息
Express express = new Express();

/**
* 监控公里数变化线程
*/
private class kmThread implements Runnable {
@Override
public void run() {
express.waitKm();
}
}

/**
* 监控地址变化线程
*/
private class siteThread implements Runnable {
@Override
public void run() {
express.waitSite();
}
}


@Test
public void test() {
//3个监控公里数变化的线程
for (int i = 0; i < 3; i++) {
new Thread(new kmThread()).start();
}

//3个监控地址变化的线程
for (int i = 0; i < 3; i++) {
new Thread(new siteThread()).start();
}

try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
//改变公里数
express.changeKm();
}
}

如上面代码 test()方法,我们分别启动了6个线程,去等待公里数和地址的变化,然后我们改变了公里数,

最终输出结果是    Thread-0公里数发生变化,现在公里数=102

如果有多个业务场景下都需要通知线程,可以用一个锁申明初多个condition,在每个业务场景下用各自的condition。 

三  名次解释

3.1 可重入锁

就是可以被多次获取的锁

public synchronized void testA()
{
System.out.println("A方法执行业务");
testB();
}

public synchronized void testB()
{
System.out.println("B方法执行业务");
}

testA本身有一把锁,调用了testB()又一把锁,共2把锁,即多次获取了锁,叫重入锁。

 

public void lockTest() {
//获取锁
lock.lock();
try {
System.out.println("第一次获取了锁");
lock.lock();
try {
System.out.println("第二次获取了锁");
} finally {
//释放锁
lock.unlock();
}
} finally {
//释放锁
lock.unlock();
}
}

上面例子里,lock也可以获取2次锁,lock也是可重入锁

由此可见,synchronized  和Lock都属于可重入锁

 

3.2 公平锁和非公平锁

      公平锁的概念:如果在时间上,先对锁进行获取的请求一定先被满足,这个锁就是公平锁(即先等先得),反之为非公平锁(随机给锁)
一般是Lock构造器默认的非公平锁,如果需要公平锁也可指定。

效率对比
非公平锁效率更高,为什么? 请看下面解释

时刻1  A线程 获取到了锁 
时刻2  B线程此时也来请求拿锁 被阻塞,被操作系统挂起
时刻3  A线程执行完业务,释放了锁
时刻3  C线程也来请求锁

非公平锁工作机制
    此时计算机把锁给C而不是给B


公平锁工作机制
    此时计算机把锁给B而不是给A
但是此时B是挂起状态,所以首先要把线程B从挂起状态恢复为正常状态(这个过程会耗费较长的时间)

     用非公平锁,把锁给C以后,C执行完业务后,可能刚好B从挂起状态恢复为正常(有这个概率),相对公平锁来说,就有机会减少唤醒挂起线程的这段时间,
所以我们说非公平说效率更高。

当然具体用哪种要根据业务,比如某些优先级高的业务,就想要优先执行,也可以用公平锁。

 

java-锁

原文:https://www.cnblogs.com/hup666/p/11886158.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!