2

synchronized关键字可以分为对象锁和类锁两个大类。

1.对象锁

将synchronized关键字加载非static方法上:

public class testss {
    public synchronized void minus(){
        int count = 5;
        while (count >= 0){
            System.out.println(Thread.currentThread().getName() + "    " + count);
            count--;
        }
    }   
}

测试类:

public class Main {
    public static void main(String[] args) {
        testss t = new testss();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.minus();
            }
        }).start();
            new Thread(new Runnable() {
            @Override
            public void run() {
                t.minus();
            }
        }).start();
    }
}

运行结果:
因为调用的方法有synchronized关键字的存在,两个线程启动过后按顺序运行,顺序不会随机发生错乱,但是两个线程哪一个首先开始执行并不一定。

Thread-0    5
Thread-0    4
Thread-0    3
Thread-0    2
Thread-0    1
Thread-0    0
Thread-1    5
Thread-1    4
Thread-1    3
Thread-1    2
Thread-1    1
Thread-1    0

我们修改一下,测试一下当一个线程访问synchronized方法的时候,另一个线程能否访问其他的synchronized方法,我们添加一个内容一样的synchronized方法minus2(),观察执行结果:

public class testss {
    public synchronized void minus(){
        int count = 5;
        while (count >= 0){
            System.out.println(Thread.currentThread().getName() + "    " + count);
        count--;
        }
    }
    public synchronized void minus2(){
        int count = 5;
        while (count >= 0){
            System.out.println(Thread.currentThread().getName() + "    " + count);
            count--;
        }
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        testss t = new testss();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.minus();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.minus2();
            }
        }).start();
    }
}

测试结果:

Thread-0    5
Thread-0    4
Thread-0    3
Thread-0    2
Thread-0    1
Thread-0    0
Thread-1    5
Thread-1    4
Thread-1    3
Thread-1    2
Thread-1    1
Thread-1    0

会发现在一个线程访问一个对象的synchronized方法的时候,另一个线程也不能访问同一个对象的synchronized方法。,这时候如果去掉minus2()的synchronized关键字,让它变成一个非同步的方法,那么执行的结果又是乱序的。

现在有一个问题就是为什么称添加在非static方法前面的synchronized关键字为对象锁?我们看一下代码:

class Mytest {
    public synchronized void minus(){
        int count = 5;
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "  1  " + count);
            count--;
        };
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Mytest test = new Mytest();
        final Mytest test2 = new Mytest();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test2.minus2();
            }
        });
        thread1.start();
        thread2.start();
    }
}

这时,我们设置的是两个不同的实例化对象分别调用类中的同步方法,执行的结果是:

Thread-0  1  5
Thread-1  2  5
Thread-0  1  4
Thread-1  2  4
Thread-0  1  3
Thread-1  2  3
Thread-1  2  2
Thread-1  2  1
Thread-0  1  2
Thread-0  1  1

乱序的。
也就说明了添加在普通方法前面的synchronized关键字,只能保证在同一个对象中这个方法是同步的,在不同的对象调用这个方法的时候synchronized是无法影响执行的。这也就是为什么叫对象锁。

2.类锁

类锁的用法是修饰类中的static方法,或者使用代码块,需引用当前的类。

public static synchronized void test(){
    // TODO
}


public static void test(){
    synchronized (TestSynchronized.class) {
        // TODO
    }
}

首先类锁在多个进程同时访问该同步方法的时候效果与对象锁是一致的:

class Mytest {
    public static synchronized void minus(){
        int count = 5;
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "  1  " + count);
            count--;
        };
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Mytest test = new Mytest();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus();
            }
        });
        thread1.start();
        thread2.start();
    }
}
Thread-0  1  5
Thread-0  1  4
Thread-0  1  3
Thread-0  1  2
Thread-0  1  1
Thread-1  1  5
Thread-1  1  4
Thread-1  1  3
Thread-1  1  2
Thread-1  1  1

但是当类中有一个类锁,有一个对象锁的时候,效果如何:

class Mytest {
    public static synchronized void minus(){
        int count = 5;
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "  1  " + count);
            count--;
        };
    }
    public synchronized void minus2(){
        int count = 5;
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "  2  " + count);
            count--;
        };
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Mytest test = new Mytest();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus2();
            }
        });
        thread1.start();
        thread2.start();
    }
}

结果如下:

Thread-0  1  5
Thread-0  1  4
Thread-1  2  5
Thread-0  1  3
Thread-0  1  2
Thread-0  1  1
Thread-1  2  4
Thread-1  2  3
Thread-1  2  2
Thread-1  2  1

因此可以发现对象锁和类锁是互不影响的。

再看看如果使用两个实例对象来调用同一个同步了的静态方法是否会收类锁的影响:

class Mytest {
    public static synchronized void minus(){
        int count = 5;
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "  1  " + count);
            count--;
        };
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Mytest test = new Mytest();
        final Mytest test2 = new Mytest();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                test.minus();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test2.minus();
            }
        });
        thread1.start();
        thread2.start();
    }
}

结果:

Thread-0  1  5
Thread-0  1  4
Thread-0  1  3
Thread-0  1  2
Thread-0  1  1
Thread-1  1  5
Thread-1  1  4
Thread-1  1  3
Thread-1  1  2
Thread-1  1  1

所以类锁是能覆盖到所有实例化这个类的对象的,因此就算使用不同的对象调用类中的同步的static方法,还是会受到类锁的影响。

3.总结

synchronized关键字用在普通方法上只对当前的对象的这个方法有同步作用。且当一个线程执行同步方法时,其他线程不可访问该对象的其他同步方法,但是可以访问不同步的方法。
synchronized关键字用在static方法上对任何实例化这个类的对象均有效。但是不影响对象锁方法的执行。


超人不会飞
12 声望4 粉丝

一个想去做开发的研究生