Synchronized详解(可重入、Monitor原理等)

更新时间:2023-05-18 21:18:10 阅读: 评论:0

Synchronized详解(可重⼊、Monitor原理等)
其他相关⽂章见:
介绍Synchronized之前,我们先来看⼀下线程安全的相关概念。
造成线程安全问题的主要诱因有两点,⼀是存在共享数据(也称临界资源),⼆是存在多条线程共同操作共享数据。
因此,引⼊了互斥锁的概念,即⼀个共享数据只能被⼀个线程访问,其他线程需要等待(阻塞),直⾄当前线程处理完毕释放该锁。
so,synchronized⽅法就保证了同⼀时刻只有⼀个线程对⽅法或者代码块有共享数据的操作。⽽且,synchronized保证了⼀个线程对共享变量操作的变化被其他线程看到(可以替代volatile功能)。
Synchronized就是内置锁,是java语⾔特性提供的内置锁,其获得锁和释放锁是隐式的(进⼊代码块就是获得锁,⾛出代码就是释放锁)。
urrent.locks 包中的锁是显⽰锁,需要进⾏lock和unlock。
⾸先,synchronized可以修饰类、⽅法(实例⽅法和静态⽅法)和代码块(修饰代码块实现同步),区别就是作⽤范围的不同:
修饰类的时候和修饰静态⽅法是⼀样的,都是给所有的对象加了同⼀把锁;
修饰实例⽅法时作⽤范围就是整个函数,给当前实例加锁;
修饰代码块时作⽤范围就是⼤括号内的内容,对给定的对象加锁。
其次,synchronized不能被继承,不能使⽤Synchronized关键字修饰接⼝⽅法;构造⽅法也不能⽤Synchronized。
⼏个简单理论:
当⼀个线程访问⼀个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
当⼀个线程访问对象的⼀个synchronized(this)同步代码块时,另⼀个线程仍然可以访问该对象中的⾮synchronized(this)同步代码块。
指定要给某个对象加锁,synchronized (account)
synchronized修饰实例⽅法
public synchronized void method()//修饰实例⽅法
自然课堂{
// todo
}
public void method()//同步代码块
{
synchronized(this) {
// todo
澳大利亚人英语}
}
/
/以上这两种其实都是锁定了整个⽅法的内容
Tip:另外,有关修饰实例⽅法的注意事项,看下列代码:
static int i=0;
public synchronized void increa(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increa();
}
}
考研能报几个学校public static void main(String[] args) throws InterruptedException {
AccountingSync instance=new AccountingSync();
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
/
**
* 输出结果:
* 2000000
*/
//下列main⽅法就不能实现同步,输出结果可能⼩于2000000
//原因是,此时就是两个线程分别new了两个实例,两个实例对象锁并不相同,两个线程对共享数据的操作就⽆法保证线程安全。//当前⾮共享数据是安全的。
/* public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new AccountingSyncBad());
//new新实例
Thread t2=new Thread(new AccountingSyncBad());
t1.start();
t2.start();
//join含义:当前线程A等待thread线程终⽌之后才能从thread.join()返回
t1.join();
t2.join();
System.out.println(i);
}*/
}
synchronized同步代码块
直接上代码:
static AccountingSync instance=new AccountingSync(); //注意要声明instance为静态变量
static int i=0;
@Override
public void run() {
//省略其他耗时操作....
//使⽤同步代码块对变量i进⾏同步操作,锁对象为给定的实例对象:instance
synchronized(instance){
for(int j=0;j<1000000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
守拙归田园Thread t2=new Thread(instance);
t1.start();t2.start();
t1.join();t2.join();北京植树
System.out.println(i);
}
}
另外,除了instance作为对象外,我们还可以使⽤this对象(代表当前实例)或者当前类的class对象作为锁,如下代码:
//this,当前实例对象锁
synchronized(this){
for(int j=0;j<1000000;j++){
i++;
}
}
//class对象锁,相当于修饰了静态⽅法
synchronized(AccountingSync.class){
for(int j=0;j<1000000;j++){
i++;
}
}
天冬药材synchronized作⽤于静态⽅法
当synchronized作⽤于静态⽅法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何⼀个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。
需要注意的是如果⼀个线程A调⽤⼀个实例对象的⾮static synchronized⽅法,⽽线程B需要调⽤这个实例对象所属类的静态synchronized⽅法,是允许的,不会发⽣互斥现象,因为访问静态 synchronized ⽅法占⽤的锁是当前类的class对象,⽽访问⾮静态synchronized ⽅法占⽤的锁是当前实例对象锁。
public class AccountingSyncClass implements Runnable{
static int i=0;
/**
* 作⽤于静态⽅法,锁是当前class对象,也就是
* AccountingSyncClass类对应的class对象
*/
public static synchronized void increa(){
i++;
}
/**
* ⾮静态,其对象锁是当前实例对象,如果别的线程调⽤该⽅法,不⼀定发⽣互斥
* 但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)
*/
public synchronized void increa4Obj(){
船的量词i++;立德树人的理解和感悟
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increa();
}
}
public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new AccountingSyncClass());
//new新实例
Thread t2=new Thread(new AccountingSyncClass());
//启动线程
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
//操作结果还是i为2000000
}
synchronized的可重⼊性
synchronized是可重⼊锁:
当⼀个线程再次请求⾃⼰持有对象锁的临界资源时,这种情况属于重⼊锁。
在java中synchronized是基于原⼦性的内部锁机制,是可重⼊的,因此在⼀个线程调⽤synchronized⽅法的同时在其⽅法体内部调⽤该对象另⼀个synchronized⽅法,也就是说⼀个线程得到⼀个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重⼊性。
public class AccountingSync implements Runnable{
static AccountingSync instance=new AccountingSync();
static int i=0;
static int j=0;
@Override
public void run() {
for(int j=0;j<1000000;j++){
//this,当前实例对象锁
synchronized(this){
i++;
increa();    //允许。synchronized的可重⼊性
}
}
}
public synchronized void increa(){
j++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
需要特别注意另外⼀种情况,当⼦类继承⽗类时,⼦类也是可以通过可重⼊锁调⽤⽗类的同步⽅法。
注意由于synchronized是基于monitor实现的,因此每次重⼊,monitor中的计数器仍会加1。
这个就是可重⼊锁的实现原理:

本文发布于:2023-05-18 21:18:10,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/914238.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   对象   实例   操作   静态   共享
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图