Java多线程之synchronized关键字

线程同步

为什么要使用线程同步呢?当有多个线程同时操作一个可共享的资源时,如果线程A修改完数据之后,还没来得及把新结果返回,此时线程B又来操作此数据,则此时线程B就获取到了错误的数据,得到的结果就可能出现严重错误。因此我们引入线程同步,当线程A在操作数据时,不允许其他线程操作此数据,待A线程工作完成之后在允许其他线程工作,这样就不会出现数据的混乱的现象了。

一、synchronized关键字的作用及范围

1、synchronized关键字作用在实例方法上:

设置工具类

public class SynchronizeUtil1 {

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

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

测试

public class SynchronizeTest1 {
    public static void main(String[] args) {

        final SynchronizeUtil1 synchronizeUtil1 = new SynchronizeUtil1();

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                synchronizeUtil1.minus();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                synchronizeUtil1.minus();
            }
        });
        t1.start();
        t2.start();
    }
}

结果

Thread-0 count: 4
Thread-0 count: 3
Thread-0 count: 2
Thread-0 count: 1
Thread-0 count: 0
Thread-1 count: 4
Thread-1 count: 3
Thread-1 count: 2
Thread-1 count: 1
Thread-1 count: 0

因为实例方法是被synchronized关键字修饰,所以上面创建的两个线程都需要获得对象锁 一个线程获得后另一个线程需要等待。

现在有一个问题:其他线程是否能够访问此对象的其他同步方法或者非同步方法呢?

经过测试得知,其他线程不能够访问此对象的其他同步方法,但是却可以调用此对象的非同步方法。

2、当synchronized关键字放在静态方法上的时候,看下面实例:

public class SynchronizeUtil1 {

    public static synchronized void minus(){
        int count = 5;
        for (int i = 0; i < 5; i++){
            count--;
            System.out.println(Thread.currentThread().getName() + " count: " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static synchronized void minus1(){
        int count = 5;
        for (int i = 0; i < 5; i++){
            count--;
            System.out.println(Thread.currentThread().getName() + " count: " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void minus2(){
        int count = 5;
        for (int i = 0; i < 5; i++){
            count--;
            System.out.println(Thread.currentThread().getName() + " count: " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果

Thread-0 count: 4
Thread-1 count: 4
Thread-0 count: 3
Thread-1 count: 3
Thread-1 count: 2
Thread-0 count: 2
Thread-1 count: 1
Thread-0 count: 1
Thread-0 count: 0
Thread-1 count: 0
Thread-2 count: 4
Thread-2 count: 3
Thread-2 count: 2
Thread-2 count: 1
Thread-2 count: 0

看到结果可以得知,synchronized修饰static方法和实例方法作用及范围是一样的。

注意: synchronized关键字不能修饰抽象类中的抽象方法,也不能是接口中的方法。

3、synchronized关键字修饰同步块

同步是一个开销很大的操作,所以我们应尽量减小synchronized关键字的修饰范围,synchronized关键字还可以修饰代码块,类似下面这种:

 public void save(int money){
        synchronized (this){
            account += money;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块。

你可能感兴趣的:(并发)