美文网首页
线程同步synchronized(4)

线程同步synchronized(4)

作者: hxj688699 | 来源:发表于2018-07-19 17:16 被阅读0次

    前言

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

    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)

          本文链接:https://www.haomeiwen.com/subject/ucjwpftx.html