美文网首页JAVA基础
Synchronized详解

Synchronized详解

作者: phoenixsky | 来源:发表于2016-05-19 16:04 被阅读252次

什么是Synchronized,有什么用?

Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

使用方式

1. 修饰对象方法(也就是非静态方法)

如果一个线程正在访问这个对象中synchronized修饰的方法时,那么其他试图访问该对象任何synchronized修饰的方法的线程都将被阻塞.

很抽象,没关系,继续往下.

/**
 * 对象方法加synchronized关键字
 */
class SyncDemo {
    synchronized void exec1() {
        for (int i = 0; i < 5; i++) {
            Thread.sleep(100);//省略try_catch
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized void exec2() {
        for (int i = 0; i < 5; i++) {
            Thread.sleep(100);//省略try_catch
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

同一个对象,同一个方法

public class SyncDemoTest {
    public static void main(String[] args) {
      //先将对象实例化,两个线程用的是一个对象
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread2").start();
    }
}

运行结果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec1-->thread2-->0
exec1-->thread2-->1
exec1-->thread2-->2
exec1-->thread2-->3
exec1-->thread2-->4

同一个对象,不同方法

public class SyncDemoTest {
    public static void main(String[] args) {
      //先将对象实例化,两个线程用的是一个对象
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec2();
            }
        }, "thread2").start();
    }
}

运行结果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

从结果中我们会发现,当exec1()执行的时候,exec2()必须要等待exec1()执行完成后才会执行.

说明: 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

结论

当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。

注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系。

one more thing

代码中构造两个线程时,分别在run方法里传入不同的SyncDemo1对象,那么两个线程的执行之间将没有什么制约关系。

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec2();
            }
        }, "thread2").start();
    }

2. 修饰静态方法

当一个synchronized关键字修饰的方法同时又被static修饰,之前说过,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁。

一个类不管生成多少个对象,它们所对应的是同一个Class对象。

/**
 * 静态方法加synchronized关键字
 */
class SyncDemo {
  //加入静态修饰
    synchronized static void exec1() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
    //加入静态修饰
    synchronized static void exec2() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

public class SyncDemoTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SyncDemo.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                SyncDemo.exec2();
            }
        }, "thread2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec1();
            }
        }, "thread3").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec2();
            }
        }, "thread4").start();
    }
}

运行结果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec2-->thread4-->0
exec2-->thread4-->1

exec2-->thread4-->2
exec2-->thread4-->3
exec2-->thread4-->4
exec1-->thread3-->0
exec1-->thread3-->1
exec1-->thread3-->2
exec1-->thread3-->3
exec1-->thread3-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

从结果中我们会发现,4个线程的调用时依次执行的

如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始

3. 修饰代码块

如下,

对象锁

    void exec() {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println("exec-->" + Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

其效果等同于修饰对象方法.this作为对象锁.

类锁

    static void exec() {
        synchronized (SyncDemo.class) {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println("exec-->" + Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

其效果等同于修饰静态方法.

4. 混合修饰

class SyncDemo {
    synchronized void exec() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec -->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized static void exec1() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized static void exec2() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

public class SyncDemoTest {
    public static void main(String[] args) {
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec();
            }
        }, "thread ").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec2();
            }
        }, "thread2").start();
    }
}

运行结果

exec -->thread -->0
exec1-->thread1-->0
exec -->thread -->1
exec1-->thread1-->1
exec -->thread -->2
exec1-->thread1-->2
exec -->thread -->3
exec1-->thread1-->3
exec -->thread -->4
exec1-->thread1-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

从结果来看,对象锁的thread和类锁的thread1 两个线程交替进行.

说明对象锁跟类的锁之间没有相互制约关系

Tips

  1. synchronized方法与synchronized代码块的区别

synchronized methods(){} 与synchronized(this){}之间没有什么区别,只是 synchronized methods(){} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率

  1. synchronized关键字是不能继承的

  2. 使用synchronized关键字解决线程的同步问题会带来一些执行效率上的问题。

JDK 5.0引入了这样一个包:java.util.concurrent:

简述

  1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是类.

  2. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

  3. 类的锁跟对象的锁之间没有制约关系.

参考

  1. synchronized关键字详解

相关文章

  • 多线程使用之-----synchronized/wait/not

    参考文献: java synchronized详解 synchronized 修饰方法 synchronized ...

  • Java基础-Synchronized详解一

    Java中的Synchronized详解 1.Synchronized简介 Synchronized简称同步锁。 ...

  • java基础----Synchronized、Lock的区别与V

    引用了 Lock与synchronized 的区别 详解synchronized与Lock的区别与使用 Java并...

  • Synchronized 原理详解

    title: synchronized原理详解date: 2019-11-27author:...

  • Day26 Synchronized

    synchronized原理详解synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,...

  • Synchronized 详解

    转载https://www.jianshu.com/p/29854dc7bd86面试题:主线程执行10次,子线程接...

  • synchronized 详解

    问题 那到底使用synchronized关键字是不是就是一个监视器? 不懂,理解的话可以向我发邮件gaomengj...

  • synchronized详解

    第一种,锁在方法上,这种很好理解 第二种,锁变量 关于这个锁我一开始理解的是在执行完synchronized代码块...

  • Synchronized 详解

    synchronized关键字采用对代码块/方法体加锁的方式解决Java中多线程访问同一个资源时,引起的资源冲突问...

  • Synchronized详解

    什么是Synchronized,有什么用? Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保...

网友评论

    本文标题:Synchronized详解

    本文链接:https://www.haomeiwen.com/subject/qsjgrttx.html