线程同步synchronized(4)

前言

为了控制多个线程对共享资源(内存、文件、数据库等)的并发访问,避免访问冲突,使得共享资源被安全有序的进行访问,引入了线程“同步”机制。

synchronized原理

java中每个对象有且仅有一个同步锁。不同线程对同步锁的访问是互斥的,线程通过synchronized关键字获得同步锁,从而实现线程同步。也就是说,任何时候对象的同步锁只能被一个线程持有,通过持有同步锁实现对共享资源的互斥访问。

synchronized基本原则

  • 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的synchronized方法或代码块的访问都被阻塞。
  • 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程可以访问该对象的非同步方法及非同步代码块。
  • 当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的其他synchronized方法或代码块的访问都被阻塞。

以上原则都在进一步强调说明,多个线程只有获取同一个对象的同步锁,才能实现互斥访问。否则可并发访问。

原则一、原则三

一、当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的synchronized方法或代码块的访问都被阻塞。三、当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程对该对象的其他synchronized方法或代码块的访问都被阻塞。示例代码:

public class SyncDemo {
    public synchronized void instanceSyncA() {
        print("instanceSyncA");
    }

    public synchronized void instanceSyncB() {
        print("instanceSyncB");
    }

    public void instanceSyncBlock() {
        synchronized (this) {
            print("instanceSyncBlock");
        }
    }

    public static synchronized void staticSyncA() {
        print("staticSyncA");
    }

    public static synchronized void staticSyncB() {
        print("staticSyncB");
    }

    public void staticSyncBlock() {
        synchronized (SyncDemo.class) {
            print("staticSyncBlock");
        }
    }

    public void noSync() {
        print("noSync");
    }

    private static void print(String calledName) {
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + calledName);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

以下示例代码为获取SyncDemo实例对象的同步锁:

public class SyncTest {
    /**
     * 线程互斥访问同一个实例同步锁
     */
    @Test
    public void instanceSync() {
        SyncDemo syncDemo = new SyncDemo();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncA();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncA();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncB();
            }
        });

        Thread thread4 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncBlock();
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        //以下代码为避免主线程退出,而导致测试提前结束
        try {
            thread1.join();
            thread2.join();
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
        }
    }
}

运行结果:

Thread-0 : instanceSyncA
Thread-0 : instanceSyncA
Thread-3 : instanceSyncBlock
Thread-3 : instanceSyncBlock
Thread-2 : instanceSyncB
Thread-2 : instanceSyncB
Thread-1 : instanceSyncA
Thread-1 : instanceSyncA

结果说明这四个线程是互斥访问SyncDemo实例同步锁的,public synchronized void instanceSyncA()、public synchronized void instanceSyncB()这两个同步方法及synchronized (this)同步代码块获取的是同一个同步锁。线程thread1调用instanceSyncA取得同步锁,导致thread2调用instanceSyncA阻塞,满足原则一;同样,线程thread1调用instanceSyncA取得同步锁导致线程thread3、线程thread4阻塞,满足原则三。

原则二

当一个线程访问某个对象的synchronized方法或synchronized代码块时,其他线程可以访问该对象的非同步方法及非同步代码块。示例代码:

public class SyncTest {
    /**
     * 其他线程仍可访问非同步代码
     */
    @Test
    public void syncAndnone() {
        SyncDemo syncDemo = new SyncDemo();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncA();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.noSync();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.noSync();
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();

        try {
            thread1.join();
            thread2.join();
            thread3.join();
        } catch (InterruptedException e) {
        }
    }
}

运行结果:

Thread-1 : noSync
Thread-2 : noSync
Thread-0 : instanceSyncA
Thread-1 : noSync
Thread-0 : instanceSyncA
Thread-2 : noSync

结果说明这三个线程没有获取同一个同步锁,是可以并发执行的。thread1调用instanceSyncA取得同步锁,由于noSync并不是同步方法,因此并没有造成线程thread2和线程thread3的阻塞。

实例锁和全局锁

文章开头已经强调,多个线程只有获取同一个同步锁,才能进行互斥访问
实例锁是指锁在对象的实例上,一个对象有多个实例,也就意味着有多个同步锁,锁有效范围是同一个实例。不同线程分别获取不同实例上的锁是不能实现互斥访问的。
全局锁是指锁在类对象上或是static关键字修饰的类变量上,锁有效范围是所有对象实例及类方法,全局锁用static synchronized修饰方法或是synchronized(类对象/类变量)同步代码块表示。代码示例:

public class SyncTest {
    /**
     * 全局锁
     */
    @Test
    public void staticSync() {
        SyncDemo syncDemo = new SyncDemo();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                SyncDemo.staticSyncA();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.staticSyncB();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.staticSyncBlock();
            }
        });

        Thread thread4 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.instanceSyncA();
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

        try {
            thread1.join();
            thread2.join();
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
        }
    }
}

运行结果

Thread-0 : staticSyncA
Thread-3 : instanceSyncA
Thread-3 : instanceSyncA
Thread-0 : staticSyncA
Thread-2 : staticSyncBlock
Thread-2 : staticSyncBlock
Thread-1 : staticSyncB
Thread-1 : staticSyncB

线程thread1、thread2和thread3运行结果说明全局锁对类方法及实例都生效。线程thread4与线程thread1并发执行,说明获取的不是同一个锁,线程thread1获取的是全局锁,导致线程thread2和thread3阻塞;而thread4获取的是实例锁。

你可能感兴趣的:(线程同步synchronized(4))