码迷,mamicode.com
首页 > 其他好文 > 详细

synchronized原理

时间:2018-04-02 23:53:51      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:操作   ring   ati   font   inf   lock   必须   enter   实现类   

在多线程中同时进行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是线程休眠并不释放锁



synchronized原理

标签:操作   ring   ati   font   inf   lock   必须   enter   实现类   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!