JAVA Synchronized

参考 / References :https://blog.csdn.net/mulanlong/article/details/84566016

最近刷leetcode时刷到了concurrency题目,所以深入学习下synchronized

1.概念
synchronized可以修饰代码块,方法,静态类和类

2.修饰代码块:

public class SyncThread implements Runnable {

    private static int count;

    public SyncThread() {
        count = 0;
    }

    @Override
    public void run() {
        synchronized (this) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
   
    public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(thread, "2");
        thread1.start();
        thread2.start();
    }
}

结果输出如下:

1 : 0
1 : 1
1 : 2
1 : 3
1 : 4
2 : 5
2 : 6
2 : 7
2 : 8
2 : 9

由于两个子线程都是访问同一个syncThread,所以必须等线程1访问完,释放对象锁后,线程2才能访问。

如果把代码改成访问两个不同的线程:

public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        SyncThread threadd = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(threadd, "2");
        thread1.start();
        thread2.start();
    }

结果输出如下:

1 : 0
2 : 1
2 : 2
1 : 3
2 : 4
1 : 5
2 : 6
1 : 7
2 : 8
1 : 9

可以看到线程1与线程2并发执行了。synchronized只锁定对象,每个对象只有一个锁与之关联,所以修改后代码中的两把锁不会形成互斥。
synchronized(this):代表使用类生成的对象作为锁,同时以对象为阻塞。
synchronized(SyncThread.class): 代表使用类的字节码作为锁,所有的对象都公用一个锁,线程会阻塞。
修改后代码如下:

@Override
    public void run() {
        synchronized (SyncThread.class) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

结果输出如下:

1 : 0
1 : 1
1 : 2
1 : 3
1 : 4
2 : 5
2 : 6
2 : 7
2 : 8
2 : 9

如果此时在此类中new一个对象锁,代码如下:

private Object lock = new Object();

    @Override
    public void run() {
        synchronized (lock) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

运行结果如下:

2 : 0
1 : 1
1 : 2
2 : 3
1 : 5
2 : 4
1 : 7
2 : 6
1 : 8
2 : 8

可以看到由于new一个新的对象的时候,也同时new一个新的锁,所以两个线程并没有公用一把锁,因此线程会交替执行。如果线程分别来自两个对象,还会出现数据异常(指的是两个线程同时访问count导致数据还没来得及跟新)。

倘若上述代码中的lock是static修饰的呢?
结果如下:

2 : 0
2 : 1
2 : 2
2 : 3
2 : 4
1 : 5
1 : 6
1 : 7
1 : 8
1 : 9

由于static有如下特性:
① static 修饰的成员(字段/方法),随着所在类的加载而加载
当 JVM 把字节码加载进入 JVM 的时候,static 修饰的成员已经在内存中了
② 优先于对象的存在
对象是被手动通过 new 关键字创造出来的
③ static 修饰的成员被该类型的所有对象所共享
根据该类创建出来的任何对象,都可以访问 static 成员。

所以该static修饰的lock是所有对象共享的,因此必须等一个线程执行完,另一个线程才能执行。

3.可用volatile字段修饰private volatile int count;这样可以锁上全部,volatile修饰的关键字一旦修改会使得线程中的变量缓存失效,重新从数据中读取。

4.还可以对函数上锁:

private static int count;

    public SyncThread() {
        count = 0;
    }
    
    public synchronized int setCount() {
        this.count++;
        return count;
    }

    @Override
    public void run() {

            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + setCount());
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }

    public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        SyncThread threadd = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(threadd, "2");
        thread1.start();
        thread2.start();
    }

结果如下:

1 : 1
2 : 2
1 : 3
2 : 4
2 : 5
1 : 6
2 : 7
1 : 8
2 : 9
1 : 10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章