浅析Java 并发编程中的synchronized

 更新时间:2020年12月03日 09:00:04   作者:拾阶求上  
这篇文章主要介绍了Java 并发编程中的synchronized的相关资料,帮助大家更好的理解和学习Java并发编程,感兴趣的朋友可以了解下

synchronized关键字,我们一般称之为“同步锁”,用它来修饰需要同步的方法和需要同步代码块,默认是当前对象作为锁的对象。在用synchronized修饰类时(或者修饰静态方法),默认是当前类的Class对象作为锁的对象,故存在着方法锁、对象锁、类锁这样的概念。

一、没有设置线程同步的情况

先给出以下代码感受下代码执行的时候为什么需要同步?代码可能比较枯燥,配上业务理解起来就会舒服很多,学生军训,有三列,每列5人,需要报数,每个线程负责每一列报数。

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff() {
		for(int i=0; i<5; i++) {
			num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {se.numberOff();}, "线程"+i).start();
		}
	}
}

执行结果如下:

线程1:1
线程2:2
线程1:3
线程3:4
.......

之所以出现这种情况,是因为三个线程是异步的,没有同步。
对应的业务场景就是,在第一列没有完成报数的时候,其他队列抢报了,这在现实中是不允许的,所以需要类似于synchronized等具有同步功能的关键字粉末登场。

二、方法同步锁

当报数方法加上synchronized关键字之后,就会一列一列的报数。

protected synchronized void numberOff() {
	for(int i=0; i<5; i++) {
		num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
	}
}

执行结果如下:

线程1:1
线程1:2
线程1:3
线程1:4
......

写到这里还是要从技术层面讲下原理,当一个线程执行带有synchronized关键字的方法时,该线程会在该方法处设置一个锁(其他线程打不开这个锁,只能在外边等该线程释放掉该锁,一般都是执行完所有代码后主动释放锁),表示此方法是当前线程独占的,对应到上述业务中就是一次只能有一个队列报数。

三、对象锁

改进后的代码用到了一个对象锁,该对象锁默认是当前对象,上述代码等同于以下代码:

protected void numberOff() {
	synchronized (this) {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

当多个线程用一个对象锁,各个线程可以达到同步的作用,如果每个线程都用自己的对象锁,那么synchronized就失去了同步的作用。如以下代码:

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff() {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
			}
		}
	}
}
public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {new SynchronizedExample().numberOff();}, "队列"+i).start();
		}
	}
}

执行结果如下:

线程1:1
线程2:2
线程1:3
线程3:4
.......

有读者会说不同线程执行的是不同对象中的方法,肯定达不到同步的效果,也对,也很有道理,接着看如下代码:

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff(Object lock) {
		synchronized (lock) {
			for (int i = 0; i < 5; i++) {
				num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
			}
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {se.numberOff(new Object());}, "队列"+i).start();
		}
	}
}

执行结果如下:

线程1:1
线程2:2
线程1:3
线程3:4
.......

四、类锁

对于上述问题,读者应该得出一个结论,要想达到同步的效果,必须用同一个锁,此时类锁可以粉末登场。看如下代码:

protected void numberOff(Object lock) {
	synchronized (SynchronizedExample.class) {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

上述代码可以达到同步的效果。

五、静态锁

静态锁是针对静态方法而言,当一个静态方法中有synchronized关键字时,默认的是使用当前类字节码对象作为锁。代码示例如下:

class SynchronizedExample {
	protected static int num = 0;
	protected synchronized static void numberOff() {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i <= 3; i++) {
			new Thread(() -> { new SynchronizedExample().numberOff(); }, "队列" + i).start();
		}
	}
}

六、线程池实现

最后用线程池将上述代码写一下

package ioo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class SynchronizedExample {
	protected static int num = 0;
	protected synchronized static void numberOff() {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		ExecutorService executorService = Executors.newCachedThreadPool();
		for(int i=1; i<=3; i++) {
			executorService.execute(() -> new SynchronizedExample().numberOff());
		}
	}
}

以上就是浅析Java 并发编程中的synchronized的详细内容,更多关于Java 并发编程 synchronized的资料请关注脚本之家其它相关文章!

相关文章

  • Java让多线程按顺序执行的几种方法

    Java让多线程按顺序执行的几种方法

    本文主要介绍了Java让多线程按顺序执行的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 解决Mybatis-Plus更新方法不更新NULL字段的问题

    解决Mybatis-Plus更新方法不更新NULL字段的问题

    这篇文章主要介绍了解决Mybatis-Plus更新方法不更新NULL字段的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • java基础之 “==”与“equals”区别详解

    java基础之 “==”与“equals”区别详解

    这篇文章主要介绍了java基础之 “==”与“equals”区别详解,需要的朋友可以参考下
    2020-02-02
  • Java线程中start和run方法全面解析

    Java线程中start和run方法全面解析

    这篇文章主要介绍了Java线程中start和run方法的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08
  • Java基础之数组超详细知识总结

    Java基础之数组超详细知识总结

    这篇文章主要介绍了Java基础之数组详解,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-05-05
  • SpringCloud Gateway 利用 Mysql 实现动态路由的方法

    SpringCloud Gateway 利用 Mysql 实现动态路由的方法

    这篇文章主要介绍了SpringCloud Gateway 利用 Mysql 实现动态路由的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • JAVA中StringBuffer与String的区别解析

    JAVA中StringBuffer与String的区别解析

    这篇文章主要介绍了JAVA中StringBuffer与String的区别解析,需要的朋友可以参考下
    2014-02-02
  • springmvc和js前端的数据传递和接收方式(两种)

    springmvc和js前端的数据传递和接收方式(两种)

    本文介绍了springmvc和js前端的数据传递和接收方式(两种),详细的介绍了两种方式,一种是json格式传递,另一种是Map传递,具有一定的参考价值,有兴趣的可以了解一下
    2017-12-12
  • Java设计模式中的策略模式详细解析

    Java设计模式中的策略模式详细解析

    这篇文章主要介绍了Java设计模式中的策略模式详细解析,所谓策略模式,指的是做某一件事时有多种选择(即策略),且不同的策略之间相互独立,而且无论使用哪种策略,得到的结果都是相同的,需要的朋友可以参考下
    2023-12-12
  • Java设计模式之观察者模式(Observer模式)介绍

    Java设计模式之观察者模式(Observer模式)介绍

    这篇文章主要介绍了Java设计模式之观察者模式(Observer模式)介绍,Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解,需要的朋友可以参考下
    2015-03-03

最新评论