java线程概述3--synchronized重要字,原理以及对应的锁版权声明
原创在多线程编程中,synchronized关键字非常常见。当我们需要“同步”操作时,我们经常需要这个关键字来锁定代码块或方法。是synchronized一次只能由一个线程访问的锁定代码块。
以上是很多人的理解,当然,也是我以前的synchronized对关键词的简单理解,其实上述观点存在一定的偏差。在参考了许多文章并自己测试了相关代码之后,我认为有必要记录自己的代码synchronized在此过程中,将简要介绍对关键词的一些理解synchronized关键词的具体实现原理。
一、synchronized:synchronized它是锁代码块还是锁对象?
synchronized它具有同步功能,或者更准确地说,它具有互斥锁定功能。那么,它是否锁定了相关的代码块或对象数据?答案是锁定对象。以下来自synchronized从修改方法和特定代码块的修改两方面理解该结论:synchronized用于锁定对象。
1、synchronized修饰方法
不用说,让我们简单地看看测试代码:
代码一

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test1 test1 = new Test1(); new Thread(new Runnable() { public void run() { try { test1.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
new Thread(new Runnable() {
public void run() {
test1.secondMethod();
}
}).start();
}
class Test1{ public synchronized void firstMethod() throws InterruptedException{ System.out.println("firstMethod"); Thread.sleep(2000); } public void secondMethod(){ System.out.println("secondMethod"); } }

输出结果:firstMethod secondMethod 或者 secondMethod firstMethod
然后,看代码二。

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test1 test1 = new Test1(); new Thread(new Runnable() { public void run() { try { test1.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { test1.thirdMethod();
}
}).start();
}
}
class Test1{ public synchronized void firstMethod() throws InterruptedException{ System.out.println("firstMethod"); Thread.sleep(2000); } public synchronized void thirdMethod(){ System.out.println("thirdMethod"); } }

结果总是:firstMethod thirdMethod
因此,我们可以得出以下结论(在理解这个结论之前,读者可以假设每个对象都有一个锁对象,这将在后面解释。synchronized将解释关键词的原理):
synchronize当修饰一个方法(非静态方法,稍后将讨论静态方法)时,这意味着当一个线程执行该方法时,它会锁定当前对象,而其他线程无法调用包含该对象的对象。synchronized关键字,因为这些线程的这些方法仅在您希望获得对象的锁时执行。
具体到上述示例:
在代码一中,到期secondMethod方法没有synchronized关键字修饰,该方法在不获取当前对象的锁的情况下执行,因此secondMethod和firstMethod执行是并行的;
在代码2中,firstMethod和thirdMethod方法均有synchronized关键字修饰,这两个方法是在可以获得当前对象的锁的前提下执行的,因此这两个不能同时执行,因为同一个对象中只有一个锁。
ok在理解了上面的示例之后,估计读者会更简单地理解下面的代码。嗯,上面的代码:
代码3:修改静态方法时(只有一个方法具有static修饰)

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test2 test2 = new Test2(); new Thread(new Runnable() { public void run() { try { test2.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { test2.thirdMethod();
}
}).start();
}
}
class Test2{ public static synchronized void firstMethod() throws InterruptedException{ System.out.println("firstMethod"); Thread.sleep(2000); } public synchronized void thirdMethod(){ System.out.println("thirdMethod"); } }

执行结果为:firstMethod _thirdMethod 或者_thirdMethod firstMethod
代码四:(都有static修饰)

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test2 test2 = new Test2(); new Thread(new Runnable() { public void run() { try { test2.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { test2.thirdMethod();
}
}).start();
}
}
class Test2{ public static synchronized void firstMethod() throws InterruptedException{ System.out.println("firstMethod"); Thread.sleep(2000); } public static synchronized void thirdMethod(){ System.out.println("thirdMethod"); } }

执行结果为:firstMethod thirdMethod
从以上结果我们可以知道:synchronized当您修饰静态方法时,它会锁定Class实例对象。
所以代码三到期firstMethod和thirdMethod锁定对象不同,因此可以并行执行;代码4中的两个方法都被锁定。class对象,因此无法并行执行。
好吧,上面一大块文字,用一句话概括如下:
synchronized当修饰静态方法时,它表示锁class对象当修饰动态方法时,这意味着锁定当前对象(this)。
2、synchronized装饰代码块
事实上,上述解释已经明确指出了一个事实:synchronized关键字意味着当前代码锁定了一个对象。,synchronizedModified)代码块需要执行,必须获得对象的锁,即等待。synchronized释放锁(实际上,它是为了更改代码块以执行),只有在synchronized装饰代码块时,必须显式地指出它锁定了哪个对象而已。上代码:
代码五:

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test3 test3 = new Test3(); new Thread(new Runnable() { public void run() { try { test3.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { test3.thirdMethod();
}
}).start();
}
}
class Test3{ public void firstMethod() throws InterruptedException{ synchronized (this) { System.out.println("firstMethod"); Thread.sleep(2000); } } public void thirdMethod(){ synchronized (this) { System.out.println("thirdMethod"); } } }

上述代码和代码25在执行效果上有任何差异,功能相同。
当然与直接修改方法相比,synchronized装饰代码块时,this它可以被其他对象替换,我们锁定对象的代码块可以更小。
好吧,上述一堆废话实际上归根结底是为了说明一个事实:synchronized修饰的代码,表示对象被锁定,如果要执行其他线程的方法(强调其他线程,为什么稍后),则关键字修改的其他线程方法也必须获得锁定,即synchronized代码块已完成执行。
以上对其他线程的强调意味着如果同一个线程synchronized方法,您不需要获取锁,这是合理的,请具体查看以下示例估计以了解我所说的内容:

public class SynchronizedTest1 { public static void main(String [] argStrings){ final Test4 test4 = new Test4(); new Thread(new Runnable() { public void run() { try { test4.firstMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }
class Test4{ public synchronized void firstMethod() throws InterruptedException{ System.out.println("firstMethod"); Thread.sleep(2000); secondMethod(); } public synchronized void secondMethod(){ System.out.println("secondMethod"); } }

结果是: firstMethod secondMethod
嗯,我也认为我在胡说八道,事实上这是显而易见的结果:synchronized如果修饰的方法彼此嵌套调用,则无需在同一线程中等待前一个方法。synchronized块执行,毕竟,这将陷入死循环。获取锁或锁定对象适用于不同的线程。
二、synchronized原理详解
1首先,了解几个锁的概念:对象锁(也称为权重锁)、轻量级锁、偏置锁
A对象锁定(重量锁定)
在多线程环境中,大多数(以及为什么大多数装饰将在后面描述)每个对象都有一个monitor对象(关于monitor特定可以查看jdk api文档),该对象实际上就是我们所解释的synchronized与关键字对应的锁。该对象锁负责管理访问该对象的所有线程,具体的管理模型图可以在下图中找到:

对于访问对象的线程,在并发的情况下,不获取锁(即对象访问)的线程将是monitor丢进list这个队列等待(当然,旋转锁除外,稍后将详细解释什么是旋转锁)。所谓的公平锁定和不公平锁定。notify唤醒list当等待线程进入时,list前者是公平的,首先等待的线程首先获得锁。后者是不公平的,它与等待锁的线程无关。
B、轻量锁
显然,权重锁为每个对象保留一个权重。monitor对象,开销必须很大,jvm对此进行了一些优化,这就是为什么会出现轻量级锁。
轻量级锁定可能是一个概念:虽然程序是一个多线程环境,但如果访问了当前对象,则对象将不会new一个monitor,但在对象的头部有一个标识字段(看起来是两位二进制数字)来表示锁,这是一个轻量级锁。当线程试图访问对象时,该线程会将当前线程的私有空间中的相应锁标识符复制到对象的头部。如果修改成功,则意味着只有一个线程访问该对象,并且该对象继续使用轻量级锁。如果发现轻量级锁已被其他线程锁定,jvm轻质锁升级为重量锁。
C、偏向锁
偏置锁是一种重量比轻的锁:当jvm发现当前程序是,但当线程正在运行时,变量将对对象进行有偏锁定,当然,一旦发现存在synchronized多线程执行场景,jvm它将把偏置锁升级为轻量级锁。
2、synchronized如何实现
在理解了上面提到的几个锁的概念之后synchronized实施原则非常简单。
在多线程环境中,对象对应于monitor管理需要访问对象的所有线程,monitor将有一个变量存储在synchronized获取其中一个线程中的次数。synchronized代码块采集monitor之后变量的数量增加1,synchronized嵌套案例也是如此:有多少synchronized,对应的变量值是多少,如果您想获取monitor,必须等到当前拥有monitor所有线程synchronized所有块都被执行,并且每个块都执行。synchronized块,标识变量的值减1,变为0当其他线程可以开始抓取锁时。
因此,开展了所有管理工作monitor完成了,它是synchronized实施的核心。
3补充其他知识
A、自旋锁
如上所述,monitor当管理每个被阻塞的线程时,它们会被放入一个队列,该队列用于非自旋锁。monitor当设置为自旋锁锁定时,线程不会挂起到队列中,而是执行循环,直到到达。monitor访问权。那么旋转锁的需求是什么呢?事实上,线程暂停和唤醒的调度过程非常昂贵。cpu,当线程在很短的时间内持有锁时,我们不需要挂起等待线程,这将导致频繁的挂起和唤醒线程,浪费cpu资源。当然,自旋锁中的线程在执行循环时肯定会消耗资源,因此如何选择仍然取决于调度线程还是原地循环的成本。
此外,对于上述情况,有一种称为自适应自旋锁的锁,它可能解决了上面提到的一些问题;
B,自适应旋转锁
自适应自旋锁实际上是添加到自旋锁中的自旋数计数:当线程在一定数量的周期后没有获得锁时,它会被抛到队列中挂断。
好的,synchronized总结到此结束。明天,我将总结另一篇关于多线程异步框架的文章。那我也欢迎你来参观!
此外,本票文章的不足之处也请您提出建议,欢迎拆除!
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123


