snchronized的用法

1.synchronized用法

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。

2.修饰一个代码块

  • 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

    package com.zt;
    
    public class T_001 {
    
        public static void main(String[] args) {
            SyncThread syncThread = new SyncThread();
            Thread thread1 = new Thread(syncThread, "SyncThread1");
            Thread thread2 = new Thread(syncThread, "SyncThread2");
            thread1.start();
            thread2.start();
        }
    }
    
    
    class SyncThread implements Runnable {
        private static int count;
    
        public SyncThread() {
            count = 0;
        }
    
        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 int getCount() {
            return count;
        }
    }
    

    使用关键字synchronized运行结果

    snchronized的用法

    不使用关键字synchronized运行结果

    snchronized的用法

    snchronized的用法

    当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。

    把SyncThread的调用稍微改一下

    snchronized的用法

    snchronized的用法

    会发现thread1和thread2同时在执行。这是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联

3.修饰一个方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将的run方法改成如下的方式,实现的效果一样。

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

运行结果 :

snchronized的用法

4.修饰一个静态的方法

public synchronized static void method() {}
我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

public class T_002 {
    public static void main(String[] args) {
        System.out.println("使用关键字静态synchronized");
        Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
        Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
        thread1.start();
        thread2.start();
    }
}

class SyncThread implements Runnable {
    private static int count;

    public SyncThread() {
        count = 0;
    }

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

    public void run() {
        method();
    }
}

运行结果

snchronized的用法

thread1和thread虽然执行的是2个不同对象,但是执行的时候保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,相当于2个线程持有了同一个对象的锁

5.修饰一个类

修饰一个类和修饰静态方法一样,都是作用于class对象上,不做演示

6.总结

  1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是类对象,该类所有对象同一把锁。
  2. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  3. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制
上一篇:C#进阶——从应用上理解异步编程的作用(async / await)


下一篇:第八章:(3)CyclicBarrier 循环栅栏