synchronized用法详解

Posted lwx521

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了synchronized用法详解相关的知识,希望对你有一定的参考价值。

1、介绍

Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍可以访问该object中的非加锁代码块。

2、修饰对象

2.1、修饰this,当前对象,这里的this指的是执行这段代码的对象,synchronized得到的锁就是this这个对象的锁。

线程thread1 访问对象testSy1的带有同步代码块的add()时,其他线程可以访问该对象的add()方法吗?

public class TestSy1 implements Runnable{
    private int number;

    TestSy1(){
        number = 0;
    }

    public void add(){
        synchronized (this){
            for(int i=0;i<4;i++){
                try{
                    System.out.println(Thread.currentThread().getName()+":thread:"+(number++));
                    Thread.sleep(500);
                }catch (Exception e){
                    System.out.println("异常");
                }
            }
        }
        System.out.println("add");
    }

    public void show(){
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 非同步:" + number);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        System.out.println("show");
    }

@Override
public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ add(); } } public static void main(String[] args){ TestSy1 testSy1 = new TestSy1(); Thread thread1 = new Thread(testSy1,"thread1"); Thread thread2 = new Thread(testSy1,"thread2"); thread1.start(); thread2.start(); } }

结果如下:

技术分享图片
thread1:thread:0
thread1:thread:1
thread1:thread:2
thread1:thread:3
thread2:thread:4
thread2:thread:5
thread2:thread:6
thread2:thread:7
可见,其他线程不能访问add()方法。
View Code

修改一下run()

 @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            add();
        }else{
            show();
        }
    }

结果如下:

thread1:thread:0
thread2 非同步:1
thread1:thread:1
thread2 非同步:2
thread2 非同步:2
thread1:thread:2
thread1:thread:3
thread2 非同步:4
thread2 非同步:4
其他线程可以访问该对象的show()方法。

那其他线程可以访问,其他对象的同步方法吗?

public class TestSy1 implements Runnable{
    private int number;

    TestSy1(){
        number = 0;
    }

    public void add(){
        synchronized (this){
            for(int i=0;i<4;i++){
                try{
                    System.out.println(Thread.currentThread().getName()+":thread:"+(number++));
                    Thread.sleep(500);
                }catch (Exception e){
                    System.out.println("异常");
                }
            }
        }
        System.out.println("add");
    }

    public void show(){
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 非同步:" + number);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        System.out.println("show");
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            add();
        }else{
            add();
        }
    }


    public static void main(String[] args){
        TestSy1 testSy1 = new TestSy1();
        TestSy1 testSy2 = new TestSy1();
        Thread thread1 = new Thread(testSy1,"thread1");
        Thread thread2 = new Thread(testSy2,"thread2");
        thread1.start();
        thread2.start();
    }
}

结果如下:

技术分享图片
thread1:thread:0
thread2:thread:0
thread1:thread:1
thread2:thread:1
thread1:thread:2
thread2:thread:2
thread1:thread:3
thread2:thread:3
View Code

总结:修饰this和修饰非静态方法一样。线程A访问对象A的带有同步代码块的方法A时,其他线程可以访问该对象的非同步方法和其他对象的所有方法。

3、修饰方法

3.1、修饰非静态方法

public class TestMethod implements Runnable{
    private static int number;

    TestMethod(){
        number = 0;
    }

public synchronized void m1(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("异常"); } } } public void m2(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("异常"); } } }

  @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            m1();
        }else{
            m1();
        }
    }

public static void main(String[] args){ TestMethod testMethod = new TestMethod(); Thread thread1 = new Thread(testMethod,"thread1"); Thread thread2 = new Thread(testMethod,"thread2"); thread1.start(); thread2.start(); } }

上述代码中,synchronized 修饰了非静态方法m1(),然后生成两个线程分别访问对象testMethod的m1()方法,结果如下:

技术分享图片
thread1:0
thread1:1
thread1:2
thread2:3
thread2:4
thread2:5
可见,synchronized修饰非静态方法时,线程thread1访问testMethod对象的同步方法m1()时,其他线程不能访问testMethod对象的同步方法m1(),那它可以访问testMethod的非同步方法m2()吗?
View Code

对上述代码的run()方法进行修改

 @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            m1();
        }else{
            m2();//让thread2访问非同步方法m2()
} }

结果如下:

技术分享图片
thread1:0
thread2:1
thread1:2
thread2:3
thread1:4
thread2:5
可见,线程thread2可以访问对象testMethod的非同步方法m2(),那线程thread2又可以访问其他对象的同步和非同步方法吗?
View Code
package p02;

public class TestMethod implements Runnable{
    private static int number;

    TestMethod(){
        number = 0;
    }


    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            m1();
        }else{
            m1();
        }
    }

    public synchronized void m1(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public  void m2(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public static void main(String[] args){
        TestMethod testMethod = new TestMethod();
        TestMethod testMethod1 = new TestMethod();
        Thread thread1  = new Thread(testMethod,"thread1");
        Thread thread2 = new Thread(testMethod1,"thread2");
        thread1.start();
        thread2.start();
    }
}

结果如下:

技术分享图片
thread1:0
thread2:1
thread1:2
thread2:3
thread2:4
thread1:5
可见,其他线程可以访问对象testMethod1的同步方法。
View Code

总结:

修饰非静态方法时,线程A访问对象A的非静态同步方法A时,其他线程可以访问该对象的非同步方法以及其他对象的任何方法

3.2、修饰静态方法

因为静态方法是属于类的,不是对象的,所以就不测试其他对象了,感兴趣的话可以自己试一试。

public class TestMethod implements Runnable{
    private static int number;

    TestMethod(){
        number = 0;
    }


    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            m1();
        }else{
            m1();
        }
    }

    public static synchronized void m1(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public void m2(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public static void main(String[] args){
        TestMethod testMethod = new TestMethod();
        Thread thread1  = new Thread(testMethod,"thread1");
        Thread thread2 = new Thread(testMethod,"thread2");
        thread1.start();
        thread2.start();
    }
}

上述代码,m1()是静态同步方法,当两个线程同时访问该方法时会发生什么?

thread1:0
thread1:1
thread1:2
thread2:3
thread2:4
thread2:5
当线程thread1访问静态同步方法m1()时,其他线程不能访问m1(),可以访问m2(),结果如下:

  thread1:0
  thread2:1
  thread1:2
  thread2:3
  thread1:4
  thread2:5

如果m2()是非静态同步方法呢?

public class TestMethod implements Runnable{
    private static int number;

    TestMethod(){
        number = 0;
    }


    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            m1();
        }else{
            m2();
        }
    }

    public static synchronized void m1(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public synchronized void m2(){
        for(int i=0;i<3;i++){
            try {
                System.out.println(Thread.currentThread().getName()+":"+(number++));
                Thread.sleep(200);
            }catch (Exception e){
                System.out.println("异常");
            }
        }
    }


    public static void main(String[] args){
        TestMethod testMethod = new TestMethod();
        Thread thread1  = new Thread(testMethod,"thread1");
        Thread thread2 = new Thread(testMethod,"thread2");
        thread1.start();
        thread2.start();
    }
}

上述代码中,线程thread1访问静态同步方法m1(),线程thread2 访问非静态同步方法m2(),结果如下:

技术分享图片
thread1:0
thread2:1
thread1:2
thread2:3
thread2:4
thread1:5
线程thread1访问静态同步方法m1()时,线程thread2可以访问非静态同步方法m2()
View Code

总结:修饰静态方法时,作用于类,同时作用于该类的所有对象。所以,线程A访问静态同步方法时,其他线程可以访问非静态同步方法和非同步方法,不可以访问静态同步方法。

4、修饰类

synchronized不可以直接修饰类,但是可以通过synchronized(类名.class)作用于类。修饰类和修饰静态方法一样,也是作用于该类的所有对象。

例子1

public class TestSy1 implements Runnable{
    private int number;

    TestSy1(){
        number = 0;
    }

    public void add(){
        synchronized (TestSy1.class){
            for(int i=0;i<4;i++){
                try{
                    System.out.println(Thread.currentThread().getName()+":thread:"+(number++));
                    Thread.sleep(500);
                }catch (Exception e){
                    System.out.println("异常");
                }
            }
        }
        System.out.println("add");
    }

    public void show(){
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 非同步:" + number);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        System.out.println("show");
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            add();
        }else{
            add();
        }
    }


    public static void main(String[] args){
        TestSy1 testSy1 = new TestSy1();
        Thread thread1 = new Thread(testSy1,"thread1");
        Thread thread2 = new Thread(testSy1,"thread2");
        thread1.start();
        thread2.start();
    }
}

结果如下:

技术分享图片
thread1:thread:0
thread1:thread:1
thread1:thread:2
thread1:thread:3
thread2:thread:4
thread2:thread:5
thread2:thread:6
thread2:thread:7
线程A访问带有synchronized(类名.class)的代码的方法时,其他线程不能方法该方法。
View Code

例子2

线程A访问带有synchronized (TestSy1.class)的add()方法,其他线程访问非静态同步方法。

public class TestSy1 implements Runnable{
    private int number;

    TestSy1(){
        number = 0;
    }

    public void add(){
        synchronized (TestSy1.class){
            for(int i=0;i<4;i++){
                try{
                    System.out.println(Thread.currentThread().getName()+":thread:"+(number++));
                    Thread.sleep(500);
                }catch (Exception e){
                    System.out.println("异常");
                }
            }
        }
        System.out.println("add");
    }

    public synchronized void show(){
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 非同步:" + number);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        System.out.println("show");
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        if(name.equalsIgnoreCase("thread1")){
            add();
        }else{
            show();
        }
    }


    public static void main(String[] args){
        TestSy1 testSy1 = new TestSy1();
        TestSy1 testSy2 = new TestSy1();
        Thread thread1 = new Thread(testSy1,"thread1");
        Thread thread2 = new Thread(testSy1,"thread2");
        thread1.start();
        thread2.start();
    }
}

结果如下:

技术分享图片
thread1:thread:0
thread2 非同步:1
thread1:thread:1
thread2 非同步:2
thread1:thread:2
thread2 非同步:3
thread1:thread:3
thread2 非同步:4
thread2 非同步:4
其他线程可以访问非静态同步方法,非同步方法更不要说了。
View Code

总结:

如果一个方法内带有synchronized (类名.class)这样的代码块,这个方法就相当于静态同步方法。

线程A方法该类的静态同步方法时,其他线程不可以访问静态同步方法,但是可以访问非静态同步方法和非同步方法。

 














以上是关于synchronized用法详解的主要内容,如果未能解决你的问题,请参考以下文章

Java 中 synchronized的用法详解

Synchronized详解

Java synchronized 关键字详解

详解synchronized锁的各种用法及注意事项

Java synchronized 关键字详解

synchronized学习