当前位置:首页 > 前端设计 > 正文

synchronized是公平锁吗,公平锁和非公平锁原理

synchronized是公平锁吗,公平锁和非公平锁原理

今天给各位分享synchronized是公平锁吗的知识,其中也会对公平锁和非公平锁原理进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!什么是条件锁...

今天给各位分享synchronized是公平锁吗的知识,其中也会对公平锁和非公平锁原理进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

什么是条件锁,读写锁,自旋锁,可重入锁

展开全部

自旋锁(Spinlock)

自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是

否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。其作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远

高于互斥锁。虽然它的效率比互斥锁高,但是它也有些不足之处:

1、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。

2、在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如copy_to_user()、copy_from_user()、kmalloc()等。

因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。

两种锁的加锁原理

互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。

自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。

互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和

Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞

(blocking),Core0会在此时进行上下文切换(Context

Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而自旋锁则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在

Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

两种锁的区别

互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。

两种锁的应用

互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑

1临界区有IO操作

2临界区代码复杂或者循环量大

3临界区竞争非常激烈

4单核处理器

至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。

lock与Syntronized的区别

转自自:

java并发之Lock与synchronized的区别

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

两者在锁的相关概念上区别:

1.可重入锁

如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

看下面这段代码就明白了:

1

2

3

4

5

6

7

8

9

classMyClass

{

publicsynchronizedvoidmethod1()

{

method2();

}

publicsynchronizedvoidmethod2()

{

}

}

上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。

而由于synchronized和Lock都具备可重入性,所以不会发生上述现象。

2.可中断锁

可中断锁:顾名思义,就是可以相应中断的锁。

在Java中,synchronized就不是可中断锁,而Lock是可中断锁。

如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性。

3.公平锁

公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。

非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。

在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。

而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

看一下这2个类的源代码就清楚了:

在ReentrantLock中定义了2个静态内部类,一个是NotFairSync,一个是FairSync,分别用来实现非公平锁和公平锁。

我们可以在创建ReentrantLock对象时,通过以下方式来设置锁的公平性:

1

ReentrantLock

lock=newReentrantLock(true);

如果参数为true表示为公平锁,为fasle为非公平锁。默认情况下,如果使用无参构造器,则是非公平锁。

另外在ReentrantLock类中定义了很多方法,比如:

isFair()//判断锁是否是公平锁

isLocked()//判断锁是否被任何线程获取了

isHeldByCurrentThread()//判断锁是否被当前线程获取了

hasQueuedThreads()//判断是否有线程在等待该锁

在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁。不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。

4.读写锁

读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。

正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

可以通过readLock()获取读锁,通过writeLock()获取写锁。

性能比较

在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。Brian

Goetz对这两种锁在JDK1.5、单核处理器及双Xeon处理器环境下做了一组吞吐量对比的实验,发现多线程环境下,synchronized的吞吐量下降的非常严重,而ReentrankLock则能基本保持在同一个比较稳定的水平上。但与其说ReetrantLock性能好,倒不如说synchronized还有非常大的优化余地,于是到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。

Spring如何更好地解决线程安全问题

线程安全问题概述卖票问题分析单窗口卖票

一个窗口(单线程)卖100张票没有问题

单线程程序是不会出现线程安全问题的

多个窗口卖不同的票

3个窗口一起卖票,卖的票不同,也不会出现问题

多线程程序,没有访问共享数据,不会产生问题

多个窗口卖相同的票

3个窗口卖的票是一样的,就会出现安全问题

多线程访问了共享的数据,会产生线程安全问题

线程安全问题代码实现

模拟卖票案例

创建3个线程,同时开启,对共享的票进行出售

线程安全问题原理分析

分析:线程安全问题正常是不允许产生的,我们可以让一个线程在访问共享数据的时候,无论是否失去了cpu的执行权;让其他的线程只能等待,等待当前线程卖完票,其他线程在进行卖票。

解决线程安全问题办法1-synchronized同步代码块

同步代码块:synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

使用synchronized同步代码块格式:

synchronized(锁对象){可能会出现线程安全问题的代码(访问了共享数据的代码)}

代码实现如下:

注意:

代码块中的锁对象,可以使用任意的对象。但是必须保证多个线程使用的锁对象是同一个。锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行。同步技术原理分析

同步技术原理:

使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器

3个线程一起抢夺cpu的执行权,谁抢到了谁执行run方法进行卖票。

t0抢到了cpu的执行权,执行run方法,遇到synchronized代码块这时t0会检查synchronized代码块是否有锁对象

发现有,就会获取到锁对象,进入到同步中执行

t1抢到了cpu的执行权,执行run方法,遇到synchronized代码块这时t1会检查synchronized代码块是否有锁对象

发现没有,t1就会进入到阻塞状态,会一直等待t0线程归还锁对象,t0线程执行完同步中的代码,会把锁对象归还给同步代码块t1才能获取到锁对象进入到同步中执行

总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步。

解决线程安全问题办法2-synchronized普通同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

格式:

publicsynchronizedvoidpayTicket(){可能会出现线程安全问题的代码(访问了共享数据的代码)}

代码实现:

分析:定义一个同步方法,同步方法也会把方法内部的代码锁住,只让一个线程执行。

同步方法的锁对象是谁?

就是实现类对象newRunnableImpl(),也是就是this,所以同步方法是锁定的this对象。

解决线程安全问题办法3-synchronized静态同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

格式:

publicstaticsynchronizedvoidpayTicket(){可能会出现线程安全问题的代码(访问了共享数据的代码)}

代码实现:

分析:静态的同步方法锁对象是谁?

不能是this,this是创建对象之后产生的,静态方法优先于对象

静态方法的锁对象是本类的class属性–>class文件对象(反射)。

解决线程安全问题办法4-Lock锁

Lock接口中的方法:

publicvoidlock():加同步锁。publicvoidunlock():释放同步锁

使用步骤:

在成员位置创建一个ReentrantLock对象在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

代码实现:

分析:java.util.concurrent.locks.Lock接口

Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

公平锁、非公平锁的创建方式:

锁绑定多个条件,一个ReentrantLock对象可以同时绑定多个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。ReentrantLock和Synchronized的区别

相同点:

它们都是加锁方式同步;都是重入锁;阻塞式的同步;也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善);

如果你还想了解更多这方面的信息,记得收藏关注本站。

最新文章