Java实现线程同步方法及原理详解
作者:main(0) 发布时间:2021-07-29 21:28:13
标签:Java,线程,同步
一、概述
无论是什么语言,在多线程编程中,常常会遇到多个线同时操作程某个变量(读/写),如果读/写不同步,则会造成不符合预期的结果。
例如:线程A和线程B并发运行,都操作变量X,若线程A对变量X进行赋上一个新值,线程B仍然使用变量X之前的值,很明显线程B使用的X不是我们想要的值了。
Java提供了三种机制,解决上述问题,实现线程同步:
同步代码块
synchronized(锁对象){
// 这里添加受保护的数据操作
}
同步方法
静态同步方法:synchronized修饰的静态方法,它的同步锁是当前方法所在类的字节码对象
public static synchronized void staticMethod(){
}
非静态同步方法:synchronized修饰的非静态方法,它的同步锁即为this
public synchronize void method(){
}
锁机制
// 以可重入锁举例
Lock lock = new ReentrantLock(/*fail*/);
// fail:
// true表示使用公平锁,即线程等待拿到锁的时间越久,越容易拿到锁
// false表示使用非公平锁,线程拿到锁全靠运气。。。cpu时间片轮到哪个线程,哪个线程就能获取锁
lock.lock();
// 这里添加受保护的数据操作
lock.unlock();
个人理解:其实无论哪种机制实现线程同步,本质上都是加锁->操作数据->解锁的过程。同步代码块是针对{}中,同步方法是针对整个方法。其ReentrantLock类提供的lock和unlock和C++的std::mutex提供lock和unlock类似
二、测试用例
同步代码块测试类
package base.synchronize;
public class SynchronizeBlock implements Runnable {
private int num = 100;
@Override
public void run() {
while (num > 1) {
synchronized (this) {
// 同步代码块,只有拿到锁,才有cpu执行权
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
同步方法测试类
package base.synchronize;
public class SynchronizeMethod implements Runnable {
private int num = 100;
public static int staticNum = 100;
boolean useStaticMethod;
public SynchronizeMethod(boolean useStaticMethodToTest) {
this.useStaticMethod = useStaticMethodToTest;
}
// 对于非静态方法,同步锁对象即this
public synchronized void method() {
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
}
// 对于静态方法,同步锁对象是当前方法所在类的字节码对象
public synchronized static void staticMethod() {
System.out.println("Static Method Thread ID:" + Thread.currentThread().getId() + "---num:" + staticNum);
staticNum--;
}
@Override
public void run() {
if (useStaticMethod) { // 测试静态同步方法
while (staticNum > 1) {
staticMethod();
}
}else{ // 测试非静态同步方法
while (num > 1){
method();
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
ReentrantLock测试类
package base.synchronize;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizeLock implements Runnable {
private Lock lock = null;
private int num = 100;
public SynchronizeLock(boolean fair){
lock = new ReentrantLock(fair); // 可重入锁
}
@Override
public void run() {
while (num > 1) {
try {
lock.lock();
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
测试三种机制的Demo
package base.synchronize;
public class Demo {
public static void main(String[] args) {
synchronizeBlockTest(); // 同步代码块
synchronizeMethodTest(); // 同步非静态方法
synchronizeStaticMethodTest(); // 同步静态方法
synchronizeLockTest(); // 可重入锁机制
}
public static void synchronizeBlockTest(){
Runnable run = new SynchronizeBlock();
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeMethodTest(){
Runnable run = new SynchronizeMethod(false);
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeStaticMethodTest() {
Runnable run = new SynchronizeMethod(true);
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeLockTest(){
Runnable run = new SynchronizeLock(false); // true:使用公平锁 false:使用非公平锁
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
}
无论哪种机制,都得到预期的效果,打印100-0
来源:https://www.cnblogs.com/main404/p/13020726.html
0
投稿
猜你喜欢
- public static String toUtf8String(String s) {
- 这篇文章主要介绍了Spring-boot的debug调试代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 本文实例为大家分享了unity shader实现光照效果的具体代码,供大家参考,具体内容如下效果图:shader被附给了球。灯光需要在属性面
- 大家好,今天尝试用swing技术写一个贪吃蛇大作战小游戏,供大家参考。 效果展示效果展示一、游戏界面二、得分情况&nb
- 一、案例场景遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样——public static
- SpringBoot项目中新增脱敏功能项目背景目前正在开发一个SpringBoot项目,此项目有Web端和微信小程序端。web端提供给工作人
- 缘起经过前面三章的入门,我们大概了解了Mybatis的主线逻辑是什么样子的,在本章中,我们将正式进入Mybatis的源码海洋。Mybatis
- 在业务开发过程中我们会遇到形形色色的注解,但是框架自有的注解并不是总能满足复杂的业务需求,我们可以自定义注解来满足我们的需求。根据注解使用的
- java try catch异常后还会继续执行吗catch中如果你没有再抛出异常,那么catch之后的代码是可以继续执行的,但是try中,报
- 之前看过一句话,说的特别好。有人问阅读源码有什么用?学习别人实现某个功能的设计思路,提高自己的编程水平。是的,大家都实现一个功能,不同的人有
- 目录什么是异常?编译时还是运行时?“受检异常”究竟可不可取?我的观点什么是异常?要了解受检异常,首先要了解什么是异常。在Java中,异常是一
- 背景在 Java 中实现线程安全的传统方式是 synchronized 关键字,虽然它提供了一定的同步能力,但它在使用上
- 前言最近发现公司的微服务项目中没有统一的批量新增方法,公司用的是MP插件,遇到批量新增都是单独去去编写xml实现,费时费力,而MP自带的批插
- 一,问题采取eureka集群、客户端通过Ribbon调用服务,Ribbon端报下列异常java.net.UnknownHostExcepti
- Java的SPI机制实例详解SPI的全名为Service Provider Interface.普通开发人员可能不熟悉,因为这个是针对厂商或
- 前言由于现在网络层已经升级到RxJava2.x相关的了,所以需要做些调整。虽然RxJava1.x和RxJava2.x同属RxJava系列,但
- 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把
- SpringBoot web项目启动后立即关闭我们在写spring boot web项目时,有时会遇到启动后立即关闭的情况,或者是无法加载某
- Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spri
- 写在前面从Java 1.0开始,引入java.io包;到Java 1.4再扩展了java.nio包;再到java 1.7又添加了新的流类,使