悲观锁:

每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,

确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。

由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。

-->Synchronized

-->写多读少

乐观锁:

每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,

但是在更新数据的时候需要判断该数据是否被别人修改过。

如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,

则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。

-->CAS(compare and set)

-->读多写少

悲观锁缺点:每一次加锁都会引起线程上下文的切换和线程调度,很影响系统运行性能

CAS:比较并修改

涉及到3个信息,一个存储位置V, 期望修改的值为A,获取原有数据为B(存储位置V获取)

步骤:

1、在存储位置V获取数据B

2、比较数据B和存储位置V的值是否相同:相同则将位置V的值修改为A

如果不相等,循环到步骤1,直至相同修改完值退出

CAS存在问题:

ABA问题:-->解决:通过版本号解决

CPU资源消耗大

Synchronized重量级锁:锁的优化

无锁-》偏向锁-》轻量级锁-》重量级锁      --》锁可以升级但不能降级

----------

|  对象头  |

----------

| 对象本身 |

----------

|填充数据  |

----------

锁优化见资料-"Java多线程-锁的介绍"

ReentrantLock锁

该 java.util.concurrent.locks包路径下的锁

公平性锁、非公平锁

公平性锁:多个线程对资源的请求按照先来后到的顺序来有序获取资源

非公平性锁:多线程对资源的获取不是按照请求的顺序来进行资源获取的,需要进行抢夺是资源获取

无参构造函数可以看出:默认是非公平性锁

public ReentrantLock() {

sync = new NonfairSync();

}

有参构造通过Boolean类型来确定使用是公平性锁还是非公平锁

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();

}

ReentrantLock是java中可重入锁的一个实现,一次只能有一个线程持有锁,也即所谓独占锁的概念

包含三个内部类:Sync、NonfairSync、FairSync,通过构造函数的参数来指定锁是否是公平的

常用方法

public class ReentrantLock implements Lock, java.io.Serializable {

private final Sync sync;

//默认是不公平锁

public ReentrantLock() {

sync = new NonfairSync();

}

//参数fair决定,true为公平锁实现,false为非公平锁实现

public ReentrantLock(boolean fair) {

sync = (fair)? new FairSync() : new NonfairSync();

}

public void lock() {

sync.lock();

}

//不公平锁

public boolean tryLock() {

return sync.nonfairTryAcquire(1);

}

//带超时时间的锁

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {

return sync.tryAcquireNanos(1, unit.toNanos(timeout));

}

public void unlock() {

sync.release(1);

}

//是否有线程等待当前锁

public final boolean hasQueuedThreads() {

return sync.hasQueuedThreads();

}

通过常用方法可知:

ReentrantLock都是把具体实现委托给内部类(Sync、NonfairSync、FairSync),

ReentrantLock的重入计数是使用AbstractQueuedSynchronizer的state属性的

state大于0表示锁被占用、等于0表示空闲,小于0则是重入次数太多导致溢出了.

ReentrantLock.Sync

static abstract class Sync extends AbstractQueuedSynchronizer {

abstract void lock();

//非公平获取,公平锁和非公平锁都需要这个方法

final boolean nonfairTryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {   //state == 0表示无锁

//CAS确保即使有多个线程竞争锁也是安全的

if (compareAndSetState(0, acquires)) {  //加锁成功

//当前哪一个线程获取到锁,将线程信息记录到AQS里面

setExclusiveOwnerThread(current);   //设置当前持有锁的线程

return true;                        //获取成功

}

} else if (current == getExclusiveOwnerThread()) {//当前线程正是锁持有者

int nextc = c acquires;

if (nextc < 0) // 被锁次数上溢(很少出现)

throw new Error("Maximum lock count exceeded");

//锁被持有的情况下,只有持有者才能更新锁保护的资源

setState(nextc);

return true;

}

return false;

}

//释放

protected final boolean tryRelease(int releases) {

int c = getState() - releases;

//只有锁的持有者才能释放锁

if (Thread.currentThread() != getExclusiveOwnerThread())

throw new IllegalMonitorStateException();

boolean free = false;

if (c == 0) {   //锁被释放

free = true;

setExclusiveOwnerThread(null);

}

setState(c);

return free;

}

//当前线程是否持有锁

protected final boolean isHeldExclusively() {

return getExclusiveOwnerThread() == Thread.currentThread();

}

final ConditionObject newCondition() {

return new ConditionObject();

}

//锁的持有者

final Thread getOwner() {

return getState() == 0 ? null : getExclusiveOwnerThread();

}

//加锁次数

final int getHoldCount() {

return isHeldExclusively() ? getState() : 0;

}

//是否上锁,根据state字段可以判断

final boolean isLocked() {

return getState() != 0;

}

}

ReentrantLock.NonfairSync【非公平锁实现】

final static class NonfairSync extends Sync {

// 执行lock,尝试立即闯入,失败就退回常规流程

final void lock() {

if (compareAndSetState(0, 1))   //比较并设置state,成功则表示获取成功

setExclusiveOwnerThread(Thread.currentThread());//锁持有者

else

acquire(1);//获取失败,进入常规流程:acquire会首先调用tryAcquire

}

protected final boolean tryAcquire(int acquires) {

return nonfairTryAcquire(acquires);

}

}

acquire的实现(AbstractQueuedSynchronizer.java)

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

ReentrantLock.FairSync(公平性锁实现)

final static class FairSync extends Sync {

//见AbstractQueuedSynchronizer.java, 4.2节有

final void lock() {

acquire(1);

}

//公平版本的tryAcquire,除非是递归调用或没有等待者或者是第一个,否则不授予访问

protected final boolean tryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {

//是等待队列的第一个等待者

if (isFirst(current) &&

compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(current);   //加锁成功

return true;

}

}

//当前线程正是线程的持有者

else if (current == getExclusiveOwnerThread()) {

int nextc = c acquires;

if (nextc < 0)  //溢出

throw new Error("Maximum lock count exceeded");

setState(nextc);

return true;

}

return false;

}

}

isFirst的实现,即等待队列为空或者当前线程为等待队列的第一个元素

final boolean isFirst(Thread current) {

Node h, s;

return ((h = head) == null ||

((s = h.next) != null && s.thread == current) ||

fullIsFirst(current));

}

lock() VS lockInterruptibly()

线程请求锁的几个方法:

lock():拿不到lock就不罢休,不然线程就一直block;

tryLock():马上返回,拿到lock就返回true,不然返回false;

tryLock(time):拿不到lock,就等一段时间,超时返回false;

线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,

此线程会被唤醒并被要求处理InterruptedException;

线程在运行中,则不会收到提醒。但是 此线程的 “中断标志”会被设置, 可以通过isInterrupted()查看并作出处理。

lockInterruptibly()和上面的第一种情况是一样的,线程在请求lock并被阻塞时,如果被interrupt,

则“此线程会被唤醒并被要求处理InterruptedException

线程间通信

Java.lang.Object

wait、notify、notifyAll

wait:让线程进入等待  -》线程状态转换中:running->waitting

notify:随机唤醒一个等待的线程

notifyAll:唤醒全部的线程

wait\notify\notifyAll必须作用与同一个对象,才能达到线程间通信的目的

wait

wait方法是Object的方法;

任意一个对象都可以调用wait方法,调用wait方法会将调用者的线程挂起,

使该线程进入一个叫wait的等待区域,直到其他线程调用同一个对象的notify方法才会重新激活调用者;

当wait方法被调用时,它会释放它所占用的锁标记,从而使线程所在对象中的synchronize数据可以被别的线程所使用,

所以wait()方法必须在同步块中使用,

notify()和notifyAll()方法都会对对象的“锁标记”进行修改,

所以都需要在同步块中进行调用,如果不在同步块中调用,虽然可以编译通过,

但是运行时会报IllegalMonitorStateException(非法的监控状态异常);

notify

notify()会通知一个处在wait()状态的线程;如果有多个线程处在wait状态,

他会随机唤醒其中一个;

notifyAll

notifyAll()会通知过所有处在wait()状态的线程,具体执行哪一个线程,

根据优先级而定;

来源:https://www.icode9.com/content-4-850401.html

(0)

相关推荐