「javalock例子」javalock原理
今天给各位分享javalock例子的知识,其中也会对javalock原理进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、java 死锁
- 2、Java多线程编程中lock.lock()是什么意思,在线等
- 3、关于java的lock和condition
- 4、java中实现同步的两种方式syschronized和lock的区别和联系
- 5、Java中Lock,tryLock,lockInterruptibly有什么区别
- 6、Java锁有哪些种类,以及区别
java 死锁
死锁
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。
由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。
//代码一
class Deadlocker {
int field_1;
private Object lock_1 = new int[1];
int field_2;
private Object lock_2 = new int[1];
public void method1(int value) {
“synchronized” (lock_1) {
“synchronized” (lock_2) {
field_1 = 0; field_2 = 0;
}
}
}
public void method2(int value) {
“synchronized” (lock_2) {
“synchronized” (lock_1) {
field_1 = 0; field_2 = 0;
}
}
}
}
参考代码一,考虑下面的过程:
◆ 一个线程(ThreadA)调用method1()。
◆ ThreadA在lock_1上同步,但允许被抢先执行。
◆ 另一个线程(ThreadB)开始执行。
◆ ThreadB调用method2()。
◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。
◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。
◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。
◆ ThreadA和ThreadB都被阻塞,程序死锁。
当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出,对于规模较大的多线程程序来说尤其如此。好的线程分析工具,例如JProbe Threadalyzer能够分析死锁并指出产生问题的代码位置。
隐性死锁
隐性死锁由于不规范的编程方式引起,但不一定每次测试运行时都会出现程序死锁的情形。由于这个原因,一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待。
加锁次序
当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁。考虑下面的情形,ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下:
◆ ThreadA获得lock_1;
◆ ThreadA被抢占,VM调度程序转到ThreadB;
◆ ThreadB获得lock_2;
◆ ThreadB被抢占,VM调度程序转到ThreadA;
◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;
◆ 调度程序转到ThreadB;
◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;
◆ ThreadA和ThreadB死锁。
必须指出的是,在代码丝毫不做变动的情况下,有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断。在这种情形下,常规的死锁检测很难确定错误所在。
占有并等待
如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二。
//代码二
public class queue {
static java.lang.Object queueLock_;
Producer producer_;
Consumer consumer_;
public class Producer {
void produce() {
while (!done) {
“synchronized” (queueLock_) {
produceItemAndAddItToQueue();
“synchronized” (consumer_) {
consumer_.notify();
}
}
}
}
public class Consumer {
consume() {
while (!done) {
“synchronized” (queueLock_) {
“synchronized” (consumer_) {
consumer_.wait();
}
removeItemFromQueueAndProcessIt();
}
}
}
}
}
}
在代码二中,Producer向队列加入一项新的内容后通知Consumer,以便它处理新的内容。问题在于,Consumer可能保持加在队列上的锁,阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁。
在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等。
要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可。
Java多线程编程中lock.lock()是什么意思,在线等
线程锁,第一个lock是一个对象Lock接口的实例对象,后面lock(),是方法,表示Lock对象执行了锁定操作,其他的线程都必须等这个线程完成,并释放锁之后,才能执行被锁住的代码块
关于java的lock和condition
1、在某些情况下,当内部锁非常不灵活时,显式锁就可以派上用场。内部条件队列有一些缺陷,每个内部锁只能有一个与之相关联的条件队列。
2、使用显式的Lock和Condition的实现类提供了一个比内部锁和条件队列更加灵活的选择。一个Condition和一个单独的Lock相关联,就像条件队列和单独的内部锁相关联一样。每个锁可以有多个等待集、中断性选择、基于时限、公平性选择等。
public interface Condition{
void await() throws InterruptedException;//相当于wait
boolean await(long time,TimeUnit unit) throws InterruptedException;
long awaitNanos(long nanosTimeout) throws InterruptedException;
void awaitUninterruptibly();
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();//相当于notify
void signalAll();//相当于notifyall
}
调用与Condition相关联的Lock的Lock.newCondition方法,可创建一个Condition.
3、有限缓存操作
@ThreadSafe
public class ConditionBoundedBufferT{
protected final Lock lock=new ReentrantLock();
private final Condition notFull=lock.newCondition();
private final Condition notEmpty=lock.newCondition();
@GuardBy("lock");
private final T[] items=(T[]) new Object[BUFFER_SIZE];
@GuardBy("lock") private int tail,head,count;
public void put(T x) throws InterruptedExceptoin{
lock.lock();
try{
while (count=items.lentgh)
notFull.await();
items[tail]=x;
if (++tail=items.length)
tail=0;
++count;
notEmpty.signal();
}
finally{lock.unlock();
}
}
public T take() throws InterruptedException{
lock.lock();
try{
while (count=items.lentgh)
notEmpty.await();
T x=items[head];
items[head]=null;
if (++head=items.length)
head=0;
--count;
notFull.signal();
return x;
}
finally{lock.unlock();
}
}
}
java中实现同步的两种方式syschronized和lock的区别和联系
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我们拿Java线程(二)中的一个例子简单的实现一下和sychronized一样的效果,代码如下:
[java] view plaincopy
public class LockTest {
public static void main(String[] args) {
final Outputter1 output = new Outputter1();
new Thread() {
public void run() {
output.output("zhangsan");
};
}.start();
new Thread() {
public void run() {
output.output("lisi");
};
}.start();
}
}
class Outputter1 {
private Lock lock = new ReentrantLock();// 锁对象
public void output(String name) {
// TODO 线程输出方法
lock.lock();// 得到锁
try {
for(int i = 0; i name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
lock.unlock();// 释放锁
}
}
}
这样就实现了和sychronized一样的同步效果,需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内。
如果说这就是Lock,那么它不能成为同步问题更完美的处理方式,下面要介绍的是读写锁(ReadWriteLock),我们会有一种需求,在对数据进行读写的时候,为了保证数据的一致性和完整性,需要读和写是互斥的,写和写是互斥的,但是读和读是不需要互斥的,这样读和读不互斥性能更高些,来看一下不考虑互斥情况的代码原型:
[java] view plaincopy
public class ReadWriteLockTest {
public static void main(String[] args) {
final Data data = new Data();
for (int i = 0; i 3; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j 5; j++) {
data.set(new Random().nextInt(30));
}
}
}).start();
}
for (int i = 0; i 3; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j 5; j++) {
data.get();
}
}
}).start();
}
}
}
class Data {
private int data;// 共享数据
public void set(int data) {
System.out.println(Thread.currentThread().getName() + "准备写入数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "写入" + this.data);
}
public void get() {
System.out.println(Thread.currentThread().getName() + "准备读取数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取" + this.data);
}
}
部分输出结果:
[java] view plaincopy
Thread-1准备写入数据
Thread-3准备读取数据
Thread-2准备写入数据
Thread-0准备写入数据
Thread-4准备读取数据
Thread-5准备读取数据
Thread-2写入12
Thread-4读取12
Thread-5读取5
Thread-1写入12
我们要实现写入和写入互斥,读取和写入互斥,读取和读取互斥,在set和get方法加入sychronized修饰符:
[java] view plaincopy
public synchronized void set(int data) {...}
public synchronized void get() {...}
部分输出结果:
[java] view plaincopy
Thread-0准备写入数据
Thread-0写入9
Thread-5准备读取数据
Thread-5读取9
Thread-5准备读取数据
Thread-5读取9
Thread-5准备读取数据
Thread-5读取9
Thread-5准备读取数据
Thread-5读取9
我们发现,虽然写入和写入互斥了,读取和写入也互斥了,但是读取和读取之间也互斥了,不能并发执行,效率较低,用读写锁实现代码如下:
[java] view plaincopy
class Data {
private int data;// 共享数据
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public void set(int data) {
rwl.writeLock().lock();// 取到写锁
try {
System.out.println(Thread.currentThread().getName() + "准备写入数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "写入" + this.data);
} finally {
rwl.writeLock().unlock();// 释放写锁
}
}
public void get() {
rwl.readLock().lock();// 取到读锁
try {
System.out.println(Thread.currentThread().getName() + "准备读取数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取" + this.data);
} finally {
rwl.readLock().unlock();// 释放读锁
}
}
}
部分输出结果:
[java] view plaincopy
Thread-4准备读取数据
Thread-3准备读取数据
Thread-5准备读取数据
Thread-5读取18
Thread-4读取18
Thread-3读取18
Thread-2准备写入数据
Thread-2写入6
Thread-2准备写入数据
Thread-2写入10
Thread-1准备写入数据
Thread-1写入22
Thread-5准备读取数据
从结果可以看出实现了我们的需求,这只是锁的基本用法,锁的机制还需要继续深入学习。
本文来自:高爽|Coder,原文地址:,转载请注明。
在java中有两种方式实现原子性操作(即同步操作):
1)使用同步关键字synchronized
2)使用lock锁机制其中也包括相应的读写锁
package com.xiaohao.test;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
public static void main(String[] args) {
final LockTest lock=new LockTest();
//输出张三
new Thread(){
public void run(){
lock.test("张三张三张三张三张三张三张三张三张三张三");
}
}.start();
//输出李四
new Thread(){
public void run(){
lock.test("李四李四李四李四李四李四李四李四李四李四");System.out.println
("\n---------------------------------------------------------------");
}
}.start();
//---------------------------------------------------------------
//模拟写入数据的
for (int i = 0; i 3; i++) {
new Thread(){
public void run() {
for (int j = 0; j 5; j++) {
// lock.set(new Random().nextInt(30));
lock.set2(new Random().nextInt(30));
}
}
}.start();
}
//模拟读取数据的
for (int i = 0; i 3; i++) {
new Thread(){
public void run() {
for (int j = 0; j 5; j++) {
// lock.get();
lock.get2();
}
}
}.start();
}
}
}
class LockTest{
private Lock lock=new ReentrantLock(); //创建普通的锁
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();//创建读写锁
private int data;// 共享数据
//实现同步的方法一 使用同步关键字 synchronized
public synchronized void test(String name){
//下面的相关操作是一个原子性的操作
// lock.lock();// 得到锁
try {
for(int i = 0; i name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
// lock.unlock();// 释放锁
}
}
//实现同步的方法二 使用lock锁机制
public void test2(String name){
//下面的相关操作是一个原子性的操作
lock.lock();// 得到锁
try {
for(int i = 0; i name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
lock.unlock();// 释放锁
}
}
//使用set方法模拟写入数据
//使用 synchronized 实现了读读,写写,读写之间的互斥 ,但读读之间的互斥是没有什么必要的
public synchronized void set(int data){
System.out.println(Thread.currentThread().getName() + "准备写入数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "写入" + this.data);
}
//使用get方法模拟读取数据
//使用 synchronized 实现了读读,写写,读写之间的互斥 ,但读读之间的互斥是没有什么必要的
public synchronized void get() {
System.out.println(Thread.currentThread().getName() + "准备读取数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取" + this.data);
}
//使用set方法模拟写入数据
//使用 读写锁实现了写写,读写之间的互斥 ,但读读之间的互斥是没有什么必要的
public void set2(int data){
readWriteLock.writeLock().lock();//获取写入锁
try{
System.out.println(Thread.currentThread().getName() + "准备写入数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "写入" + this.data);
}
finally{
readWriteLock.writeLock().unlock();
}
}
//使用get方法模拟读取数据
//使用 读写锁实现了写写,读写之间的互斥 ,但读读之间的互斥是没有什么必要的
public void get2() {
//获取相应的读锁
readWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName() + "准备读取数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取" + this.data);
}
finally{
// 释放相应的写锁
readWriteLock.readLock().unlock();
}
}
}
线程同步经典版:
package com.xiaohao.test;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test2{
public static void main(String[] args){
final LockTest2 lockTest=new LockTest2();
for(int i=0;i3;i++) {
new Thread(){
public void run(){
try {
for (int j = 0; j 3; j++) {
lockTest.setValue();
} } catch (InterruptedException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
}
}.start();
}
for(int i=0;i3;i++) {
new Thread(){
public void run(){
try {
for (int j = 0; j 3; j++) {
lockTest.getValue();
}
} catch (InterruptedException e)
{ // TODO Auto-generated catch block e.printStackTrace(); }
}
}.start();
}
}
}
class LockTest2 {
int data=0;
ReentrantReadWriteLock lock= new ReentrantReadWriteLock();// 锁对象
public void setValue() throws InterruptedException{
lock.writeLock().lock();
System.out.println("正在使用写锁......");
data=(int) (Math.random()*10);
System.out.println("正在写入:"+data);
Thread.sleep(500);
System.out.println("写锁调用完毕---------------------------");
lock.writeLock().unlock(); }
public void getValue() throws InterruptedException{
lock.readLock().lock();
System.out.println("正在使用读锁...........................................");
System.out.println("正在读入:"+data); Thread.sleep(500);
System.out.println("读锁调用完毕......");
lock.readLock().unlock();
}
}
**** 当一个线程进入了一个对象是的synchronized方法,那么其它线程还能掉否调用此对象的其它方法?
这个问题需要分几种情况进行讨论。
1)查看其它方法是否使用了同步关键字(synchronized)修饰,如果没有的话就可以调用相关的方法。
2)在当前synchronized方法中是否调用了wait方法,如果调用了,则对应的锁已经释放,可以访问了。
3)如果其它方法也使用synchronized修饰,并且当前同步方法中没有调用wait方法的话,这样是不允许访问的。
4)如果其它方法是静态方法的话,由于静态方法和对象是扯不上什么关系,对于静态同步方法而言,其对应的同步监视器为当前类的字节码
所以肯定可以访问的了。
Java中Lock,tryLock,lockInterruptibly有什么区别
Java中Lock,tryLock,lockInterruptibly的区别如下:
一、 lock()方法
使用lock()获取锁,若获取成功,标记下是该线程获取到了锁(用于锁重入),然后返回。若获取失败,这时跑一个for循环,循环中先将线程阻塞放入等待队列,当被调用signal()时线程被唤醒,这时进行锁竞争(因为默认使用的是非公平锁),如果此时用CAS获取到了锁那么就返回,如果没获取到那么再次放入等待队列,等待唤醒,如此循环。其间就算外部调用了interrupt(),循环也会继续走下去。一直到当前线程获取到了这个锁,此时才处理interrupt标志,若有,则执行 Thread.currentThread().interrupt(),结果如何取决于外层的处理。lock()最终执行的方法如下:
[java] view plain copy
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head tryAcquire(arg)) { //如果竞争得到了锁
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted; //获取成功返回interrupted标志
}
// 只修改标志位,不做其他处理
if (shouldParkAfterFailedAcquire(p, node) strongparkAndCheckInterrupt()/strong)
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
其中parkAndCheckInterrupt()调用了LockSupport.park(),该方法使用Unsafe类将进程阻塞并放入等待队列,等待唤醒,和await()有点类似。
可以看到循环中检测到了interrupt标记,但是仅做 interrupted = true 操作,直到获取到了锁,才return interrupted,然后处理如下
[java] view plain copy
public final void acquire(int arg) {
if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); // 执行Thread.currentThread().interrupt()
}
二、 lockInterruptibly()方法
和lock()相比,lockInterruptibly()只有略微的修改,for循环过程中,如果检测到interrupt标志为true,则立刻抛出InterruptedException异常,这时程序变通过异常直接返回到最外层了,又外层继续处理,因此使用lockInterruptibly()时必须捕捉异常。lockInterruptibly()最终执行的方法如下:
[java] view plain copy
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return; //获取成功返回
}
if (shouldParkAfterFailedAcquire(p, node)
parkAndCheckInterrupt())
throw new InterruptedException(); //直接抛出异常
}
} finally {
if (failed)
cancelAcquire(node);
}
}
三、 tryLock()方法
使用tryLock()尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false,告诉外层没有获取到锁,之后的操作取决于外层,代码如下:
[java] view plain copy
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
Java锁有哪些种类,以及区别
一、公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
二、可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
三、独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Java
ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。
四、互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
五、乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
六、分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
七、偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java
5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
八、自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
典型的自旋锁实现的例子,可以参考自旋锁的实现
关于javalock例子和javalock原理的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
发布于:2022-11-26,除非注明,否则均为
原创文章,转载请注明出处。