synchronized原理

在多线程中同时进行i++操作 不能保证i的原子性。i++ 可以看做是i=i+1 即先从内存中读出i的值 再设置新的值。多线程操作一个线程再刚读出i的值 另外一个线程改变了i的值则不能保证数据的一致性。

synchronized则能保证原子性。synchronized 一个线程获得锁对象则会将对象标记为锁定状态。执行完毕之后释放锁

synchronize的三种应用方式

  • 修饰实例方法
  • 修饰静态方法
  • 修饰代码块

修饰实例方法

public class Accounting implements  Runnable {
    int i=0;

    public  void  account(){
        i++;
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();
        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting);
        Thread t2=new Thread(accounting);
        t.start();
        t2.start();
        t.join();
        t2.join();
        System.out.print(accounting.getI());
    }

我们会发现这段代码不是我们所期望的4000.正如前面所说多线程下不能保证变量的原子操作

如果我们要保证原子操作加上synchronized关键字,保证一个代码块同时只能有一个线程执行

public class Accounting implements  Runnable {
    int i=0;

    public synchronized   void  account(){
        i++;
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();
        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting);
        Thread t2=new Thread(accounting);
        t.start();
        t2.start();
        t.join();
        t2.join();
        System.out.print(accounting.getI());
    }
}

这段代码account则是用当前对象(this)作为对象锁。当一个线程进入account方法。则标记锁定状态。另外一个线程等待其他线程释放  但是我们不能保证上一个线程执行完毕这个线程就能获得锁(因为有个锁竞争的机制)

修饰静态方法

public class Accounting implements  Runnable {
   static int i=0;

    public static synchronized   void  account(){
        i++;
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();
        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting);
        Thread t2=new Thread(accounting);
        t.start();
        t2.start();
        t.join();
        t2.join();
        System.out.print(accounting.getI());
    }
}
synchronized修饰静态方法 则是将当前类的class对象作为对象锁Accounting.class
public class Accounting implements  Runnable {
   static int i=0;

    public static    void  account(){
        i++;
    }
    public   synchronized void  account2(){
        i++;
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();
            account2();
        }
    }
    publaic int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting,"a1");
        Thread t2=new Thread(accounting,"a2");
        t.start();
        t2.start();
        t.join();//主线程挂起等待这个线程执行完毕在往下执行
        t2.join();
        System.out.print(accounting.getI());
    }
}

这段代码虽然account 和account2都加了synchronized关键字,但是并不能保证线程的原子性操作 account2和account并不是用的同一个对象锁。

当线程a1进入account 方法 a2线程等待阻塞等待释放。当account方法执行完毕a1释放锁 再往竞争account2的锁的时候,并不能阻止其他线程访问account方法。因为account2这个时候锁定的是accounting对象的锁。而account 使用的是Accounting.class的锁  Accounting.class锁已经被释放

修饰代码块

使用this

public class Accounting implements  Runnable {
    int i=0;
    public   void  account(){
        synchronized (this) {
             i++;
        }
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();

        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting,"a1");
        Thread t2=new Thread(accounting,"a2");
        t.start();
        t2.start();
        t.join();//主线程挂起等待这个线程执行完毕在网下执行
        t2.join();
        System.out.print(accounting.getI());
    }

使用Object

public class Accounting implements  Runnable {
    int i=0;
    Object lockObj=new Object();
    public   void  account(){
        synchronized (lockObj) {
             i++;
        }
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();

        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting,"a1");
        Thread t2=new Thread(accounting,"a2");
        t.start();
        t2.start();
        t.join();//主线程挂起等待这个线程执行完毕在网下执行
        t2.join();
        System.out.print(accounting.getI());
    }
}

使用class

.

public class Accounting implements  Runnable {
    int i=0;
    Object lockObj=new Object();
    public   void  account(){
        synchronized (Accounting.class) {
             i++;
        }
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();

        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting,"a1");
        Thread t2=new Thread(accounting,"a2");
        t.start();
        t2.start();
        t.join();//主线程挂起等待这个线程执行完毕在网下执行
        t2.join();
        System.out.print(accounting.getI());
    }
}

我们可以发现sychronized的锁对象可以是任意的 要保证原子性必须保证多线程获取的锁对象是同一个

(之前记得别人说不能用字符串作为锁对象 会导致 总是线程a1获得锁 但是我测试没有发现这样的问题)

sychronized原理

java对象在jvm内存中机构

锁对象monitor的引用则是保存在java对象头里面

实例变量保存的实例对象的所有变量信息 包括父类变量信息

monitor的实现类是ObjectMonitor

主要成员包括_WaitSet  _EntryList  _Owner 用来保存ObjectWaiter对象(每个等待锁的线程都会封装成ObjectWaiter)

当多线程同时访问一段同步代码块会首先进入_EntryList 获得锁的线程则会设置到_oWner 同时monitor对象的count+1

如果调用线程的wait方法则清空_oWner count-1 同时当前线程进入_WaitSet等待被唤醒

如果当前线程执行完毕也将清空_Owner count-1

int i=0;
    public   void  account(){
        synchronized (this) {

             i++;
        }
    }
    @Override
    public void run() {
        for (int j=0;j<2000;j++){
            account();

        }
    }
    public int getI() {
        return i;
    }
    public static void main(String[] args) throws InterruptedException {
        Accounting accounting= new Accounting();
        Thread t=new Thread(accounting,"a1");
        Thread t2=new Thread(accounting,"a2");
        t.start();
        t2.start();
        t.join();//主线程挂起等待这个线程执行完毕在网下执行
        t2.join();
        System.out.print(accounting.getI());
    }

这段代码编译后 同步代码块将变为

monitorenter//进入同步方法

同步代码块内容

monitorexit//退出同步方

monitorenter 获得锁的线程则会设置到monitor对象的_oWner 同时monitor对象的count+1

monitorexit   清空_Owner  count-1

线程中断

public void interrupt();//只能中断阻塞线程 需要用异常捕获
public  boolean isInterrupted();
public static boolean interrupted();
package com.liqiang.sychronize;

public class Accounting implements Runnable {
    int i = 0;

    public void account() {

    }

    @Override
    public void run() {
        try {
            while (true) {

                Thread.sleep(2000);

                System.out.println("1");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public int getI() {
        return i;
    }

    public static void main(String[] args) throws InterruptedException {
        Accounting accounting = new Accounting();
        Thread t = new Thread(accounting, "a1");
        t.start();
        Thread.sleep(4000);
        t.interrupt();
        System.out.println(t.isInterrupted());
        t.join();//主线程挂起等待这个线程执行完毕在往下执行

    }
}
interrupt方法只能中断阻塞线程(需要try捕获异常 否则会一直执行下去)非阻塞线程需要我们手动中断
package com.liqiang.sychronize;

public class Accounting implements Runnable {
    int i = 0;

    public void account() {

    }

    @Override
    public void run() {

            while (true) {
                    if(Thread.currentThread().isInterrupted()){
                        break;
                    }
                System.out.println("1");
            }

    }

    public int getI() {
        return i;
    }

    public static void main(String[] args) throws InterruptedException {
        Accounting accounting = new Accounting();
        Thread t = new Thread(accounting, "a1");
        t.start();
        Thread.sleep(4000);
        t.interrupt();
        System.out.println(t.isInterrupted());
        t.join();//主线程挂起等待这个线程执行完毕在网下执行

    }
}

Thread.sleep 与wait的区别  wait会将线程锁释放 线程保存到monitor的 _waitSet里面 等待被唤醒。 sleep是线程休眠并不释放锁



原文地址:https://www.cnblogs.com/LQBlog/p/8698382.html

时间: 2024-11-02 11:14:19

synchronized原理的相关文章

synchronized 原理分析

synchronized 原理分析 1. synchronized 介绍 ?? 在并发程序中,这个关键字可能是出现频率最高的一个字段,他可以避免多线程中的安全问题,对代码进行同步.同步的方式其实就是隐式的加锁,加锁过程是有 jvm 帮我们完成的,再生成的字节码中会有体现,如果反编译带有不可消除的 synchronized 关键字的代码块的 class 文件我们会发现有两个特殊的指令 monitorenter 和 monitorexit ,这两个就是进入管程和退出管程.为什么说不可消除的 sync

[JAVA修炼之路十]-JVM synchronized原理或优化

synchronized语法:1.synchronized语句:2.synchronized方法 1.monitorenter和monitorexit字节码:依赖于底层的操作系统的Mutex Lock来实现的 2.会被翻译成普通的方法调用和返回指令如:invokevirtual.areturn指令 原理:用户线程阻塞,内核线程启动,设计到用户线成与内核线程的切换,花销较大 JVM 对于锁的优化 一.偏向锁(Biased Locking) jdk1.6之后默认开启.参数开启方式:-XX:+UseB

深入分析Synchronized原理

前言 记得开始学习Java的时候,一遇到多线程情况就使用synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决多线程情况的百试不爽的良药.但是,随着学习的进行我们知道在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它. 不过,随着Javs SE 1.6对synchronized进行的各种优化后,synchroni

【Java并发编程】11.P6的offer擦肩而过就因为我不懂synchronized原理

使用 synchronized关键字是并发编程中线程同步的常用手段之一,synchronized是悲观锁,其作用有三个: 互斥性:确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须排队获得锁,可见性:保证共享变量的修改能够及时可见,获得锁的线程操作完毕后会将所数据刷新到共享内存区有序性:有效解决重排序问题,其用法也有三个:修饰实例方法修饰静态方法修饰代码块1. 修饰实例方法 synchronized关键词作用在方法的前面,用来锁定方法,其实默认锁定的是this对象. publ

java 偏向锁、轻量级锁及重量级锁synchronized原理

Java对象头与Monitor java对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的. 对象头包含两部分:Mark Word 和 Class Metadata Address 其中Mark Word在默认情况下存储着对象的HashCode.分代年龄.锁标记位等以下是32位JVM的Mark Word默认存储结构 由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本,因此考虑到JVM的空间效率,Mark Word 被设计成

synchronized的底层实现原理

转自:http://www.cnblogs.com/paddix/p/5367116.html 如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了.我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: 1 package com.paddx.test.concurrent; 2 3 public class SynchronizedDemo { 4 public void method() { 5

Java并发编程:Synchronized及其实现原理

Java并发编程系列[未完]: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题.从语法上讲,Synchronized总共有三种用法: (1)修饰普通方法 (2)修饰静态方法 (3)修饰代码块 接下

Java Synchronized及实现原理

Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题.从语法上讲,Synchronized总共有三种用法: (1)修饰普通方法 (2)修饰静态方法 (3)修饰代码块 首先来看一下没有使用同步的情况 public class SynchronizedTest { public void method1(){ System.out

java线程总结--synchronized关键字,原理以及相关的锁

在多线程编程中,synchronized关键字非常常见,当我们需要进行"同步"操作时,我们很多时候需要该该关键字对代码块或者方法进行锁定.被synchronized锁定的代码块,只能同时有一条线程访问该代码块. 上面是很多人的认识,当然也是我之前对synchronized关键字的浅显认识,其实上面的观点存在一定的偏差.在参考了很多文章以及自己动手测试过相关代码后,我觉得有必要记录下自己对synchronized关键字的一些理解,在这个过程,会简单说说synchronized关键字的具体