第一章附加:线程锁synchronized方法,synchronized块和synchronied(this)总结[通俗易懂]

第一章附加:线程锁synchronized方法,synchronized块和synchronied(this)总结[通俗易懂]第一章附加:线程锁synchronized方法,synchronized块和synchronied(this)总结

大家好,又见面了,我是你们的朋友全栈君。

本文主题转自:https://www.cnblogs.com/oracleDBA/archive/2010/05/22/1741642.html

本文主要阐述以下三个问题:

1.       synchronzied作用

2.       synchronzied语法

3.       synchronized(this)的理解

 

首先,解释一下synchronzied作用

Synchronzied关键字的作用一个词概括就是:线程同步。它可以用来修改对象中的方法,将对象加锁。相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A,没有的话,直接运行。

 

然后,讲讲synchronzied语法

Synchronzied关键字包括两种用法:synchronized 方法和 synchronized 块。

1.       synchronized 方法

如:public synchronized void accessVal(int newVal);

synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为synchronized ,以控制其对类的静态成员变量的访问。

2.       synchronized 

synchronized 方法是对整个方法进行加锁。若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

如:synchronized(syncObject) {

  //允许访问控制的代码

  }

synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

 

最后,说说synchronzied(this)。下面的几个示例阐述地非常清晰,而且只有通过demo,才能对知识点理解得更加透彻。建议在IDE中亲自运行,体验一下。

这里先说一下this这个指向问题,我们经常说this指向的是当前对象,可是相信有很多朋友迷惑当前对象到底是谁,小编也有过相同的疑惑,曾经任务当前对象是所在的类。其实不然,一下面的代码为例,其实this指向的就是这个t1,Thread t1=new Thread();在下面的ta和tb中,尽管两个线程的地址不一样,也是两个线程,但是他们持有的线程对象都是t1,这样是为了同步代码块。用一个比较经典的总结就是:钥匙在给代码加锁的那个线程手里,钥匙就是this,这里指向t1.

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

package ths;

 

public class Thread1 implements Runnable {

public void run() {

synchronized(this) {

for (int i = 0; i < 5; i++) {

System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);

}

}

}

public static void main(String[] args) {

Thread1 t1 = new Thread1();

Thread ta = new Thread(t1, "A");

Thread tb = new Thread(t1, "B");

ta.start();

tb.start();

}

}

结果:

A synchronized loop 0

A synchronized loop 1

A synchronized loop 2

A synchronized loop 3

A synchronized loop 4

B synchronized loop 0

B synchronized loop 1

B synchronized loop 2

B synchronized loop 3

B synchronized loop 4

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

package ths;

 

public class Thread2 {

public void m4t1() {

synchronized(this) {

int i = 5;

while( i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : " + i);

try {

Thread.sleep(500);

} catch (InterruptedException ie) {

}

}

}

}

public void m4t2() {

int i = 5;

while( i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : " + i);

try {

Thread.sleep(500);

} catch (InterruptedException ie) {

}

}

}

public static void main(String[] args) {

final Thread2 myt2 = new Thread2();

Thread t1 = new Thread(

new Runnable() {

public void run() {

myt2.m4t1();

}

}, "t1"

);

Thread t2 = new Thread(

new Runnable() {

public void run() {

myt2.m4t2();

}

}, "t2"

);

t1.start();

t2.start();

}

}

结果:

t1 : 4

t2 : 4

t1 : 3

t2 : 3

t1 : 2

t2 : 2

t1 : 1

t2 : 1

t1 : 0

t2 : 0

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

//修改Thread2.m4t2()方法:

 

public void m4t2() {

synchronized(this) {

int i = 5;

while( i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : " + i);

try {

Thread.sleep(500);

} catch (InterruptedException ie) {

}

}

}
}

结果:

t1 : 4

t1 : 3

t1 : 2

t1 : 1

t1 : 0

t2 : 4

t2 : 3

t2 : 2

t2 : 1

t2 : 0

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

//修改Thread2.m4t2()方法如下:

 

public synchronized void m4t2() {

int i = 5;

while( i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : " + i);

try {

Thread.sleep(500);

} catch (InterruptedException ie) {

}

}

}

结果:

t1 : 4

t1 : 3

t1 : 2

t1 : 1

t1 : 0

t2 : 4

t2 : 3

t2 : 2

t2 : 1

t2 : 0

五、以上规则对其它对象锁同样适用:

package ths;

 

public class Thread3 {

class Inner {

private void m4t1() {

int i = 5;

while(i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);

try {

Thread.sleep(500);

} catch(InterruptedException ie) {

}

}

}

private void m4t2() {

int i = 5;

while(i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);

try {

Thread.sleep(500);

} catch(InterruptedException ie) {

}

}

}

}

private void m4t1(Inner inner) {

synchronized(inner) { //使用对象锁

inner.m4t1();

}

}

private void m4t2(Inner inner) {

inner.m4t2();

}

public static void main(String[] args) {

final Thread3 myt3 = new Thread3();

final Inner inner = myt3.new Inner();

Thread t1 = new Thread(

new Runnable() {

public void run() {

myt3.m4t1(inner);

}

}, "t1"

);

Thread t2 = new Thread(

new Runnable() {

public void run() {

myt3.m4t2(inner);

}

}, "t2"

);

t1.start();

t2.start();

}

}

结果:

尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。

t1 : Inner.m4t1()=4

t2 : Inner.m4t2()=4

t1 : Inner.m4t1()=3

t2 : Inner.m4t2()=3

t1 : Inner.m4t1()=2

t2 : Inner.m4t2()=2

t1 : Inner.m4t1()=1

t2 : Inner.m4t2()=1

t1 : Inner.m4t1()=0

t2 : Inner.m4t2()=0

现在在Inner.m4t2()前面加上synchronized

private synchronized void m4t2() {

int i = 5;

while(i-- > 0) {

System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);

try {

Thread.sleep(500);

} catch(InterruptedException ie) {

}

}

}

结果:

 

尽管线程t1t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2Inner.m4t2()的访问也被阻塞,因为m4t2()Inner中的一个同步方法。

t1 : Inner.m4t1()=4

t1 : Inner.m4t1()=3

t1 : Inner.m4t1()=2

t1 : Inner.m4t1()=1

t1 : Inner.m4t1()=0

t2 : Inner.m4t2()=4

t2 : Inner.m4t2()=3

t2 : Inner.m4t2()=2

t2 : Inner.m4t2()=1

t2 : Inner.m4t2()=0

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/106126.html原文链接:https://javaforall.net

(0)
上一篇 2022年4月23日 下午6:20
下一篇 2022年4月23日 下午6:40


相关推荐

  • java gridbagconstraints_GridBagConstraints

    java gridbagconstraints_GridBagConstraintsGridBagConstraintsjavaGui|2014-05-1412:54:00|阅读1737次|评论(1)importjavax.swing.*;importjava.awt.*;publicclassdemo_7{publicstaticvoidmain(String[]args){demo_7_1test=newdemo_7_1();}}clas…

    2025年10月17日
    5
  • The package javax.swing is not accessible错误的三种解决办法,亲测有效

    The package javax.swing is not accessible错误的三种解决办法,亲测有效按照学长给出的解决方案 eclipse 中的解决办法 solution 在我们这个报错的 java 文件所在的项目中 选择对应的 JRE 系统库 右击 JRE 系统库 改变 执行环境 那一栏 我这里是因为 jdk 13 版本导入不了 javax swing 的包 所以我按照学长的建议 改成了 jdk1 8 的版本

    2026年3月19日
    2
  • C++多线程并发(五)—原子操作与无锁编程

    C++多线程并发(五)—原子操作与无锁编程一、何为原子操作前面介绍了多线程间是通过互斥锁与条件变量来保证共享数据的同步的,互斥锁主要是针对过程加锁来实现对共享资源的排他性访问。很多时候,对共享资源的访问主要是对某一数据结构的读写操作,如果数据结构本身就带有排他性访问的特性,也就相当于该数据结构自带一个细粒度的锁,对该数据结构的并发访问就能更加简单高效,这就是C++11提供的原子数据类型<atomic>。下面解释两个概念:…

    2022年6月8日
    59
  • nodejs安装与环境变量配置[通俗易懂]

    nodejs安装与环境变量配置[通俗易懂]NodeJsNode.js简介发展史特性模块下载nodejs系统环境nodejs(msi)方式的安装nodejs(zip)方式的安装环境配置Linux下安装Node测试参考资料Node.js简介Node是一个让JavaScript运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。发布于2009年5月,由R…

    2022年6月12日
    43
  • python浮雕图片_用Python来画浮雕画

    python浮雕图片_用Python来画浮雕画浮雕艺术在世界各地都可以见到,中国古代在唐朝以来就有许多浮雕效果的东西,很多的大型纪念性建筑都有这种作为装饰,常见的有花窗,龙柱等。简单的来说,浮雕就是把所要呈现的图像突起于石头表面,根据凹凸的程度不同从而形成三维的立体感。用Python画一张浮雕画,那就进行类似的原理,通过勾画图像的轮廓,并且降低周围的像素值,那就可以产生一张具有立体感的浮雕效果图片。我们可以采用相邻像素相减的方法来得到轮廓与平…

    2022年6月20日
    32
  • Android动态改变布局

    Android动态改变布局

    2021年9月4日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号