首页 > 编程语言 >java多线程基础小白指南--synchronized同步块

java多线程基础小白指南--synchronized同步块

时间:2023-01-29 17:26:59浏览次数:100  
标签:java synchronized Thread public static new 多线程 class

sychronized是java多线程非常关键的一个知识点,这篇博客将从synchronized几个用法以及代码来学习。
sychronized的作用是能够保证同一时间只有一个线程来运行这块代码,达到并发效果,如果没有保证并发的话,在多线程编码中就会产生致命问题,比如经典的i++,这也是数据库并发中经典的案例,i++并不是原子操作,分为三步,取数,操作,写数,参考这段代码,可以运行一下看下结果

点击查看代码
public class showUnsafe1 implements Runnable{
    static int i=0;
    @Override
    public void run() {
        for(int j=0;j<10000;j++){
            i++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new showUnsafe1());
        Thread thread2 = new Thread(new showUnsafe1());
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }
}

synchronized四种用法

synchronized为啥这么神奇,无它,加锁而已,不少八股文喜欢分为两种锁,一种是对象锁,一种是类锁,还可以分为方法锁,代码块锁,静态锁,class锁,我们通过代码学习他们如何使用

  • 对象锁:方法锁
    方法锁是用synchronized修饰的一个类方法,作用方法即是方法作用域,除了这个方法要同步,其余不需要
两个线程访问同一对象的同一方法
public class SynchronizedObjectMethod implements Runnable{

    private static SynchronizedObjectMethod instance=new SynchronizedObjectMethod();
    public  synchronized void method(){
        System.out.println("我是对象锁的方法修饰符形式。我叫"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    @Override
    public void run() {
        method();
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while(thread1.isAlive()||thread2.isAlive()){
        }
        System.out.println("finish");
    }
}
  • 对象锁:代码块形式
    代码块锁就是常用的同步方法块,synchronized锁住的是它里面的对象,作用域就是synchonized{}里面的代码
两个线程访问同一个对象的同步代码块
public class SynchronizedObjectCodeBlock implements Runnable{
    private static SynchronizedObjectCodeBlock instance=new SynchronizedObjectCodeBlock();
    Object lock1=new Object();
    @Override
    public void run() {
        synchronized (lock1){
            System.out.println("我是对象锁的代码块形式。我叫"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }

    }

    public static void main(String[] args){
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        while(thread1.isAlive()||thread2.isAlive()){
        }
        System.out.println("finish");
    }
}
  • 类锁:class形式
    class形式说的是synchronized()括号里使用的锁是class对象,所谓class对象指得是java文件对应的一个java.lang.class对象,所有该类生成的对象共有这个class对象(对于类加载机制可以看我另一篇文章https://www.cnblogs.com/spark-cc/p/16882268.html),所以这个锁锁住了这个类生成的所有对象
不同任务的两个线程访问同一个class对象
public class SynchronizedClassClass implements Runnable{
    private static SynchronizedClassClass instance1=new SynchronizedClassClass();
    private static SynchronizedClassClass instance2=new SynchronizedClassClass();
    public void method(){
        synchronized (SynchronizedClassClass.class){
            System.out.println("我是类锁的形式之一:修饰.class。我叫"+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"运行结束");
        }
    }
    @Override
    public void run() {
        method();
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while(thread1.isAlive()||thread2.isAlive()){
        }
        System.out.println("finish");
    }
}

在这个案例中,存在两个SynchronizedClassClass对象,但是不能同时访问同步代码

  • 类锁:static形式
    static形式说的是static修饰synchronized修饰的方法,即static synchronized methodName,作用方法还是这个类的class对象
注意静态同步方法和非静态方法
public class SynchronizedClassStatic implements Runnable{
    private static SynchronizedClassStatic instance1=new SynchronizedClassStatic();
    private static SynchronizedClassStatic instance2=new SynchronizedClassStatic();
    public static synchronized void method(){
        System.out.println("我是类锁的形式之一:加static修饰。我叫"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    public void  method2(){
        System.out.println("我是非静态方法,我叫"+Thread.currentThread().getName());
    }
    @Override
    public void run() {
        method();
        method2();
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
        while(thread1.isAlive()||thread2.isAlive()){
        }
        System.out.println("finish");
    }
}

synchronized的原理

让我们从字节码来看看synchronized的神秘面纱
我们反编译synchronized修饰object的java文件

源代码
public class Decompilation {
    private Object object=new Object();

    public void insert(Thread thread){
        synchronized (object){

        }
    }
}

反编译指令为 javap -c -v java文件的class文件

所以就是这个小小的monitorenter和monitorexit指令完成了同步操作
关于monitorenter和monitorexit的定义可以查看jvm文档(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.monitorenter) 中的注释

这是monitorenter的,注意看紫色框框起来的关键部分

这是monitorexit的
这里就解释清楚为什么synchronized上的锁是可重入的,对于更深入的理解,比如monitor的概念,cpp代码实现,我之后有机会再讲下我的理解(主要是我还没懂,不敢班门弄斧,怕了怕了)

我们再反编译synchronize修饰的同步方法,其结果是

与之前不一样,这里是在方法的访问标识上添加了ACC_SYNCHRONIZED,它在jvm文档中是这么解释

大致意思就是当调用设置了 ACC_SYNCHRONIZED 的方法时,执行线程进入监视器(monitor),然后执行这个方法,方法执行完毕后退出监视器。在执行线程拥有监视器期间,没有其他线程可以进入这个方法(这里也涉及到monitor的理解!!!)

synchornized的各种锁以及优化

之后进行补充

标签:java,synchronized,Thread,public,static,new,多线程,class
From: https://www.cnblogs.com/spark-cc/p/17069585.html

相关文章

  • JavaScript 文件处理
    JavaScript可以通过文件API实现许多常见的文件处理任务,下面是一些例子:1、读取文件内容:使用FileReaderAPI可以将文件读取为ArrayBuffer、Blob、DataURL等格式,并进......
  • HotSpot VM启动时JNI_CreateJavaVM方法执行步骤
    1、确保只有一个线程调用这个方法并且确保只创建一个HotSpotVM实例。因为HotSpotVM创建的静态数据结构无法再次初始化,所以一旦初始化达到某个确定点后,进程空间里就只能有......
  • 002-dockerfile部署java项目
    1.dockerfile展示注意,这里是将DockerFile配置文件放到了maven项目目录内,和pom.xml同级目录。自动检测的文件名为Dockerfile,使用-f指定dockerfile文件路径#Downlo......
  • 过滤和监听 c# java
    用JAVA开发WEB项目时,在处理特殊的HTTP请求,或全局处理一些页面代码时会用到监听器、过滤器,jsp--过滤器与监听器1.过滤器1.1简介过滤器是处于客户端与服务器资源文件之......
  • ORM哪家强?java,c#,php,python,go 逐一对比, 网友直呼:全面客观
    前言最近一段时间,我使用golang开发了一个新的ORM库。为了让这个库更好用,我比较研究了各语言的主流ORM库,发现有一些语言的ORM库确实很好用,而有另外一些语言的库那不是一般......
  • java常用的设计模式
     java的设计模式一、单例模式  基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。常见写法:  1.饿汉式publicclassSingleton{priva......
  • JavaScript 文件上传
    JavaScript可以使用表单提交来实现文件上传。首先,在HTML中创建一个文件输入框:<inputtype="file"id="fileInput">然后,在JavaScript中获取文件输入框的引用,并在其上......
  • JavaWeb-VUE&Element
    JavaWeb-VUE&Element1,VUE1.1概述Vue是一套前端框架,免除原生JavaScript中的DOM操作,简化书写。Mybatis是用来简化jdbc代码编写的;而VUE是前端的框架,是用来简化Ja......
  • Java百分比、BigDecimal小数互转
    1、百分比转为BigDecimal小数Stringpercent="66.60%";percent=percent.replace("%","");Floatf=Float.valueOf(percent)/100;BigDecimaldecimal=newBigDeci......
  • 如何让Java编译器帮你写代码
    作者:京东零售刘世杰导读本文结合京东监控埋点场景,对解决样板代码的技术选型方案进行分析,给出最终解决方案后,结合理论和实践进一步展开。通过关注文中的技术分析过程和技......