zoukankan      html  css  js  c++  java
  • Java多线程(三):synchronized

    多线程安全

    脏读:多个线程对同一个对象的实例变量进行修改后访问,导致读到的数据是被修改过的。

    实例

    ThreadDomain16类

    public class ThreadDomain16 {
        private int num = 0;
    
        public void addNum(String userName)
        {
            try
            {
                if ("a".equals(userName))
                {
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                }
                else
                {
                    num = 200;
                    System.out.println("b set over!");
                }
                System.out.println(userName + " num = " + num);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    

    MyThread16_0类

    public class MyThread16_0 extends Thread{
        private ThreadDomain16 td;
    
        public MyThread16_0(ThreadDomain16 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.addNum("a");
        }
    }
    

    MyThread16_1类
    启动MyThread16_0 和MyThread16_1 线程

    public class MyThread16_1 extends Thread {
        private ThreadDomain16 td;
    
        public MyThread16_1(ThreadDomain16 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.addNum("b");
        }
    
    }
    

    main方法

    public class MyThread16_main_0 {
        public static void main(String[] args)
        {
            ThreadDomain16 td = new ThreadDomain16();
            MyThread16_0 mt0 = new MyThread16_0(td);
            MyThread16_1 mt1 = new MyThread16_1(td);
            mt0.start();
            mt1.start();
        }
    }
    

    输出结果如下

    a set over!
    b set over!
    b num = 200
    a num = 200
    

    理论上应该输出a num = 100,b num = 200,结果a num = 200。
    我们来分析下运行流程,MyThread16_0线程修改num为100,此时sleep两秒,在MyThread16_0 sleep的过程中,MyThread16_1线程修改num为200。导致MyThread16_0的num也变成200。最后先输出b num = 200,再输出a num = 200。出现了脏读。
    解决该问题很简单,我们在addNum()前面加上synchronized关键字

    public synchronized void addNum(String userName)
        {
            try
            {
                if ("a".equals(userName))
                {
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                }
                else
                {
                    num = 200;
                    System.out.println("b set over!");
                }
                System.out.println(userName + " num = " + num);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    

    输出结果如下

    a set over!
    a num = 100
    b set over!
    b num = 200
    

    按序打印,synchronized修饰的方法形成了锁,哪个线程先执行了synchronized修饰的方法哪个线程就先获得了锁,其他线程必须等待,直到该线程释放锁,等待的线程获得锁方可执行。

    一个对象一个锁

    同步情况下,使用如下main方法

    public class MyThread16_main_1 {
        public static void main(String[] args)
        {
            ThreadDomain16 td0 = new ThreadDomain16();
            ThreadDomain16 td1 = new ThreadDomain16();
            MyThread16_0 mt0 = new MyThread16_0(td0);
            MyThread16_1 mt1 = new MyThread16_1(td1);
            mt0.start();
            mt1.start();
        }
    }
    

    运行结果

    a set over!
    b set over!
    b num = 200
    a num = 100
    

    出现了交叉打印,b没有等待a执行结束,说明synchronized是对象锁,而不是方法锁。这里声明了两个ThreadDomain16对象,所以有两个对象锁,不受“等待锁的释放”的制约。
    那么我们把addNum方法前面的synchronized关键字去掉,异步情况
    运行结果如下

    a set over!
    b set over!
    b num = 200
    a num = 100
    

    可以看到,没有synchronized的帮助,依然避免了脏读现象。实例变量num为每个对象独自占有。但是,这种解决方式需要声明多个对象,不推荐。

    synchronized方法与锁对象

    现在看一个例子,一个方法有synchronized修饰,另一个方法无synchronized修饰
    ThreadDomain17类

    public class ThreadDomain17 {
        public synchronized void methodA()
        {
            try
            {
                System.out.println("开始 methodA, 线程名称 = " +
                        Thread.currentThread().getName());
                Thread.sleep(5000);
                System.out.println("结束 methodA, 线程名称 = " +
                        Thread.currentThread().getName());
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    
        public void methodB()
        {
            try
            {
                System.out.println("开始 methodB, 线程名称 = " +
                        Thread.currentThread().getName());
                Thread.sleep(5000);
                System.out.println("结束 methodB, 线程名称 = " +
                        Thread.currentThread().getName());
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    

    MyThread17_0类

    public class MyThread17_0 extends Thread{
        private ThreadDomain17 td;
    
        public MyThread17_0(ThreadDomain17 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.methodA();
        }
    }
    

    MyThread17_1类

    public class MyThread17_1 extends Thread{
        private ThreadDomain17 td;
    
        public MyThread17_1(ThreadDomain17 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.methodB();
        }
    }
    

    main方法

    public class MyThread17_main {
        public static void main(String[] args) {
            ThreadDomain17 td = new ThreadDomain17();
            MyThread17_0 mt0 = new MyThread17_0(td);
            mt0.setName("A");
            MyThread17_1 mt1 = new MyThread17_1(td);
            mt1.setName("B");
            mt0.start();
            mt1.start();
        }
    }
    

    运行结果如下

    开始 methodB, 线程名称 = B
    开始 methodA, 线程名称 = A
    结束 methodA, 线程名称 = A
    结束 methodB, 线程名称 = B
    

    可以看到,非synchronized的methodB方法以异步方式被调用。
    把MyThread17_1类的run方法改成td.methodA();
    输出结果如下

    开始 methodA, 线程名称 = A
    结束 methodA, 线程名称 = A
    开始 methodA, 线程名称 = B
    结束 methodA, 线程名称 = B
    

    待执行的线程在等待synchronized方法同步再执行。
    A线程持有ThreadDomain17对象锁,B线程可以异步方式调用ThreadDomain17对象的非synchronized方法;
    A线程持有ThreadDomain17对象锁,B线程调用ThreadDomain17对象的synchronized方法需要等待;

    把MyThread17_1类的run方法改回td.methodB();把ThreadDomain17类的methodB方法加上synchronized关键字,输出结果如下

    开始 methodA, 线程名称 = A
    结束 methodA, 线程名称 = A
    开始 methodB, 线程名称 = B
    结束 methodB, 线程名称 = B
    

    这个很好理解,先执行的线程获得同步锁,等该线程必须执行完毕,等待的线程才能执行,所有的操作都是同步的。

    synchronized锁重入

    synchronized是可重入锁,当一个线程得到对象锁之后,可以再次申请对象锁并获得对象锁。
    ThreadDomain18类

    public class ThreadDomain18 {
        public synchronized void print1()
        {
            System.out.println("ThreadDomain18.print1()");
            print2();
        }
    
        public synchronized void print2()
        {
            System.out.println("ThreadDomain18.print2()");
            print3();
        }
    
        public synchronized void print3()
        {
            System.out.println("ThreadDomain18.print3()");
        }
    }
    

    MyThread18类

    public class MyThread18 extends Thread{
        public void run()
        {
            ThreadDomain18 td = new ThreadDomain18();
            td.print1();
        }
    
        public static void main(String[] args)
        {
            MyThread18 mt = new MyThread18();
            mt.start();
        }
        
    }
    

    结果如下

    ThreadDomain18.print1()
    ThreadDomain18.print2()
    ThreadDomain18.print3()
    

    结果说明,MyThread18在有锁的情况下多次获取到对象锁

    异常释放锁

    ThreadDomain19类

    public class ThreadDomain19 {
        public synchronized void testMethod()
        {
            try
            {
                System.out.println("进入 ThreadDomain19.testMethod(), currentThread = " +
                        Thread.currentThread().getName());
                long l = 5;
                while (true)
                {
                    long lo = 2 / l;
                    Thread.sleep(1000);
                    l--;
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    

    MyThread19类

    public class MyThread19 extends Thread {
        private ThreadDomain19 td;
    
        public MyThread19(ThreadDomain19 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.testMethod();
        }
    
        public static void main(String[] args)
        {
            ThreadDomain19 td = new ThreadDomain19();
            MyThread19 mt0 = new MyThread19(td);
            MyThread19 mt1 = new MyThread19(td);
            mt0.start();
            mt1.start();
        }
    }
    

    输出结果如下

    进入 ThreadDomain19.testMethod(), currentThread = Thread-1
    java.lang.ArithmeticException: / by zero
    进入 ThreadDomain19.testMethod(), currentThread = Thread-0
    	at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
    	at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)
    java.lang.ArithmeticException: / by zero
    	at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
    	at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)
    

    由上可见,一旦出现异常,锁自动释放。Thread-1刚抛完异常,此时Thread-1释放了锁,Thread-0就开始执行。

    synchronized方法块

    ThreadDomain20类

    public class ThreadDomain20 extends Thread{
        public void doLongTimeTask() throws Exception
        {
            for (int i = 0; i < 100; i++)
            {
                System.out.println("nosynchronized threadName = " +
                        Thread.currentThread().getName() + ", i = " + (i + 1));
            }
            System.out.println();
            synchronized (this){
                for (int i = 0; i < 100; i++)
                {
                    System.out.println("synchronized threadName = " +
                            Thread.currentThread().getName() + ", i = " + (i + 1));
                }
            }
        }
        
    }
    

    MyThread20类

    public class MyThread20 extends Thread{
        private ThreadDomain20 td;
    
        public MyThread20(ThreadDomain20 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            try
            {
                td.doLongTimeTask();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args)
        {
            ThreadDomain20 td = new ThreadDomain20();
            MyThread20 mt0 = new MyThread20(td);
            mt0.setName("A");
            MyThread20 mt1 = new MyThread20(td);
            mt1.setName("B");
            mt0.start();
            mt1.start();
        }
    
    }
    

    运行结果如下

    nosynchronized threadName = A, i = 1
    nosynchronized threadName = B, i = 1
    nosynchronized threadName = A, i = 2
    nosynchronized threadName = B, i = 2
    nosynchronized threadName = A, i = 3
    nosynchronized threadName = B, i = 3
    nosynchronized threadName = A, i = 4
    nosynchronized threadName = B, i = 4
    nosynchronized threadName = A, i = 5
    nosynchronized threadName = B, i = 5
    nosynchronized threadName = A, i = 6
    nosynchronized threadName = B, i = 6
    nosynchronized threadName = A, i = 7
    nosynchronized threadName = B, i = 7
    nosynchronized threadName = A, i = 8
    nosynchronized threadName = B, i = 8
    nosynchronized threadName = A, i = 9
    nosynchronized threadName = B, i = 9
    nosynchronized threadName = A, i = 10
    nosynchronized threadName = B, i = 10
    nosynchronized threadName = A, i = 11
    nosynchronized threadName = B, i = 11
    nosynchronized threadName = A, i = 12
    nosynchronized threadName = B, i = 12
    nosynchronized threadName = A, i = 13
    nosynchronized threadName = B, i = 13
    nosynchronized threadName = A, i = 14
    nosynchronized threadName = B, i = 14
    nosynchronized threadName = A, i = 15
    nosynchronized threadName = A, i = 16
    nosynchronized threadName = A, i = 17
    nosynchronized threadName = A, i = 18
    nosynchronized threadName = B, i = 15
    nosynchronized threadName = A, i = 19
    nosynchronized threadName = B, i = 16
    nosynchronized threadName = A, i = 20
    nosynchronized threadName = B, i = 17
    nosynchronized threadName = A, i = 21
    nosynchronized threadName = B, i = 18
    nosynchronized threadName = A, i = 22
    nosynchronized threadName = B, i = 19
    nosynchronized threadName = A, i = 23
    nosynchronized threadName = A, i = 24
    nosynchronized threadName = B, i = 20
    nosynchronized threadName = A, i = 25
    nosynchronized threadName = B, i = 21
    nosynchronized threadName = A, i = 26
    nosynchronized threadName = B, i = 22
    nosynchronized threadName = A, i = 27
    nosynchronized threadName = B, i = 23
    nosynchronized threadName = A, i = 28
    nosynchronized threadName = B, i = 24
    nosynchronized threadName = A, i = 29
    nosynchronized threadName = B, i = 25
    nosynchronized threadName = A, i = 30
    nosynchronized threadName = B, i = 26
    nosynchronized threadName = A, i = 31
    nosynchronized threadName = A, i = 32
    nosynchronized threadName = B, i = 27
    nosynchronized threadName = A, i = 33
    nosynchronized threadName = B, i = 28
    nosynchronized threadName = A, i = 34
    nosynchronized threadName = B, i = 29
    nosynchronized threadName = A, i = 35
    nosynchronized threadName = B, i = 30
    nosynchronized threadName = A, i = 36
    nosynchronized threadName = B, i = 31
    nosynchronized threadName = A, i = 37
    nosynchronized threadName = B, i = 32
    nosynchronized threadName = A, i = 38
    nosynchronized threadName = A, i = 39
    nosynchronized threadName = B, i = 33
    nosynchronized threadName = A, i = 40
    nosynchronized threadName = B, i = 34
    nosynchronized threadName = A, i = 41
    nosynchronized threadName = B, i = 35
    nosynchronized threadName = A, i = 42
    nosynchronized threadName = B, i = 36
    nosynchronized threadName = A, i = 43
    nosynchronized threadName = B, i = 37
    nosynchronized threadName = A, i = 44
    nosynchronized threadName = B, i = 38
    nosynchronized threadName = A, i = 45
    nosynchronized threadName = B, i = 39
    nosynchronized threadName = A, i = 46
    nosynchronized threadName = B, i = 40
    nosynchronized threadName = A, i = 47
    nosynchronized threadName = B, i = 41
    nosynchronized threadName = A, i = 48
    nosynchronized threadName = B, i = 42
    nosynchronized threadName = A, i = 49
    nosynchronized threadName = B, i = 43
    nosynchronized threadName = A, i = 50
    nosynchronized threadName = B, i = 44
    
    nosynchronized threadName = B, i = 45
    synchronized threadName = A, i = 1
    nosynchronized threadName = B, i = 46
    synchronized threadName = A, i = 2
    nosynchronized threadName = B, i = 47
    synchronized threadName = A, i = 3
    nosynchronized threadName = B, i = 48
    synchronized threadName = A, i = 4
    nosynchronized threadName = B, i = 49
    nosynchronized threadName = B, i = 50
    
    synchronized threadName = A, i = 5
    synchronized threadName = A, i = 6
    synchronized threadName = A, i = 7
    synchronized threadName = A, i = 8
    synchronized threadName = A, i = 9
    synchronized threadName = A, i = 10
    synchronized threadName = A, i = 11
    synchronized threadName = A, i = 12
    synchronized threadName = A, i = 13
    synchronized threadName = A, i = 14
    synchronized threadName = A, i = 15
    synchronized threadName = A, i = 16
    synchronized threadName = A, i = 17
    synchronized threadName = A, i = 18
    synchronized threadName = A, i = 19
    synchronized threadName = A, i = 20
    synchronized threadName = A, i = 21
    synchronized threadName = A, i = 22
    synchronized threadName = A, i = 23
    synchronized threadName = A, i = 24
    synchronized threadName = A, i = 25
    synchronized threadName = A, i = 26
    synchronized threadName = A, i = 27
    synchronized threadName = A, i = 28
    synchronized threadName = A, i = 29
    synchronized threadName = A, i = 30
    synchronized threadName = A, i = 31
    synchronized threadName = A, i = 32
    synchronized threadName = A, i = 33
    synchronized threadName = A, i = 34
    synchronized threadName = A, i = 35
    synchronized threadName = A, i = 36
    synchronized threadName = A, i = 37
    synchronized threadName = A, i = 38
    synchronized threadName = A, i = 39
    synchronized threadName = A, i = 40
    synchronized threadName = A, i = 41
    synchronized threadName = A, i = 42
    synchronized threadName = A, i = 43
    synchronized threadName = A, i = 44
    synchronized threadName = A, i = 45
    synchronized threadName = A, i = 46
    synchronized threadName = A, i = 47
    synchronized threadName = A, i = 48
    synchronized threadName = A, i = 49
    synchronized threadName = A, i = 50
    synchronized threadName = B, i = 1
    synchronized threadName = B, i = 2
    synchronized threadName = B, i = 3
    synchronized threadName = B, i = 4
    synchronized threadName = B, i = 5
    synchronized threadName = B, i = 6
    synchronized threadName = B, i = 7
    synchronized threadName = B, i = 8
    synchronized threadName = B, i = 9
    synchronized threadName = B, i = 10
    synchronized threadName = B, i = 11
    synchronized threadName = B, i = 12
    synchronized threadName = B, i = 13
    synchronized threadName = B, i = 14
    synchronized threadName = B, i = 15
    synchronized threadName = B, i = 16
    synchronized threadName = B, i = 17
    synchronized threadName = B, i = 18
    synchronized threadName = B, i = 19
    synchronized threadName = B, i = 20
    synchronized threadName = B, i = 21
    synchronized threadName = B, i = 22
    synchronized threadName = B, i = 23
    synchronized threadName = B, i = 24
    synchronized threadName = B, i = 25
    synchronized threadName = B, i = 26
    synchronized threadName = B, i = 27
    synchronized threadName = B, i = 28
    synchronized threadName = B, i = 29
    synchronized threadName = B, i = 30
    synchronized threadName = B, i = 31
    synchronized threadName = B, i = 32
    synchronized threadName = B, i = 33
    synchronized threadName = B, i = 34
    synchronized threadName = B, i = 35
    synchronized threadName = B, i = 36
    synchronized threadName = B, i = 37
    synchronized threadName = B, i = 38
    synchronized threadName = B, i = 39
    synchronized threadName = B, i = 40
    synchronized threadName = B, i = 41
    synchronized threadName = B, i = 42
    synchronized threadName = B, i = 43
    synchronized threadName = B, i = 44
    synchronized threadName = B, i = 45
    synchronized threadName = B, i = 46
    synchronized threadName = B, i = 47
    synchronized threadName = B, i = 48
    synchronized threadName = B, i = 49
    synchronized threadName = B, i = 50
    

    结论:
    A线程访问synchronized方法块的时候,B线程依然可以访问非synchronized方法块的内容;
    A线程访问synchronized方法块的时候,B线程访问synchronized方法块会被阻塞;

    synchronized方法块和synchronized方法

    synchronized关键字修饰的方法和synchronized方法块修饰的方法效果一致。synchronized方法块可以提高代码执行效率,在需要同步的地方去同步。
    ThreadDomain21类

    public class ThreadDomain21 {
        public synchronized void otherMethod()
        {
            System.out.println("----------run--otherMethod");
        }
    
        public void doLongTask()
        {
            synchronized (this)
            {
                for (int i = 0; i < 100; i++)
                {
                    System.out.println("synchronized threadName = " +
                            Thread.currentThread().getName() + ", i = " + (i + 1));
                    try
                    {
                        Thread.sleep(5);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    MyThread21_0类

    public class MyThread21_0 extends Thread{
        private ThreadDomain21 td;
    
        public MyThread21_0(ThreadDomain21 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.doLongTask();
        }
    }
    

    MyThread21_1类

    public class MyThread21_1 extends Thread{
        private ThreadDomain21 td;
    
        public MyThread21_1(ThreadDomain21 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.otherMethod();
        }
    }
    

    main方法,sleep一会,让mt0先执行。

    public class MyThread21_main {
        public static void main(String[] args) throws Exception
        {
            ThreadDomain21 td = new ThreadDomain21();
            MyThread21_0 mt0 = new MyThread21_0(td);
            mt0.setName("A");
            MyThread21_1 mt1 = new MyThread21_1(td);
            mt1.setName("B");
            mt0.start();
            Thread.sleep(100);
            mt1.start();
        }
    }
    

    输出结果如下

    synchronized threadName = A, i = 1
    synchronized threadName = A, i = 2
    synchronized threadName = A, i = 3
    synchronized threadName = A, i = 4
    synchronized threadName = A, i = 5
    synchronized threadName = A, i = 6
    synchronized threadName = A, i = 7
    synchronized threadName = A, i = 8
    synchronized threadName = A, i = 9
    synchronized threadName = A, i = 10
    synchronized threadName = A, i = 11
    synchronized threadName = A, i = 12
    synchronized threadName = A, i = 13
    synchronized threadName = A, i = 14
    synchronized threadName = A, i = 15
    synchronized threadName = A, i = 16
    synchronized threadName = A, i = 17
    synchronized threadName = A, i = 18
    synchronized threadName = A, i = 19
    synchronized threadName = A, i = 20
    synchronized threadName = A, i = 21
    synchronized threadName = A, i = 22
    synchronized threadName = A, i = 23
    ......
    synchronized threadName = A, i = 89
    synchronized threadName = A, i = 90
    synchronized threadName = A, i = 91
    synchronized threadName = A, i = 92
    synchronized threadName = A, i = 93
    synchronized threadName = A, i = 94
    synchronized threadName = A, i = 95
    synchronized threadName = A, i = 96
    synchronized threadName = A, i = 97
    synchronized threadName = A, i = 98
    synchronized threadName = A, i = 99
    synchronized threadName = A, i = 100
    ----------run--otherMethod
    

    可以看到,otherMethod方法被doLongTask方法阻塞,说明synchronized锁定了整个对象,把otherMethod方法synchronized关键字去掉,输出结果如下

    synchronized threadName = A, i = 1
    synchronized threadName = A, i = 2
    synchronized threadName = A, i = 3
    synchronized threadName = A, i = 4
    synchronized threadName = A, i = 5
    synchronized threadName = A, i = 6
    synchronized threadName = A, i = 7
    synchronized threadName = A, i = 8
    synchronized threadName = A, i = 9
    synchronized threadName = A, i = 10
    synchronized threadName = A, i = 11
    synchronized threadName = A, i = 12
    synchronized threadName = A, i = 13
    synchronized threadName = A, i = 14
    synchronized threadName = A, i = 15
    synchronized threadName = A, i = 16
    synchronized threadName = A, i = 17
    synchronized threadName = A, i = 18
    synchronized threadName = A, i = 19
    synchronized threadName = A, i = 20
    synchronized threadName = A, i = 21
    ----------run--otherMethod
    ......
    synchronized threadName = A, i = 72
    synchronized threadName = A, i = 73
    synchronized threadName = A, i = 74
    synchronized threadName = A, i = 75
    synchronized threadName = A, i = 76
    synchronized threadName = A, i = 77
    synchronized threadName = A, i = 78
    synchronized threadName = A, i = 79
    synchronized threadName = A, i = 80
    synchronized threadName = A, i = 81
    synchronized threadName = A, i = 82
    synchronized threadName = A, i = 83
    synchronized threadName = A, i = 84
    synchronized threadName = A, i = 85
    synchronized threadName = A, i = 86
    synchronized threadName = A, i = 87
    synchronized threadName = A, i = 88
    synchronized threadName = A, i = 89
    synchronized threadName = A, i = 90
    synchronized threadName = A, i = 91
    synchronized threadName = A, i = 92
    synchronized threadName = A, i = 93
    synchronized threadName = A, i = 94
    synchronized threadName = A, i = 95
    synchronized threadName = A, i = 96
    synchronized threadName = A, i = 97
    synchronized threadName = A, i = 98
    synchronized threadName = A, i = 99
    synchronized threadName = A, i = 100
    

    otherMethod方法异步执行了。
    结论如下:
    不同的方法被synchronized修饰会产生互斥,synchronized锁住的是对象;
    synchronized方法块和synchronized方法效果一致;

    synchronized(非this对象)

    ThreadDomain22类

    public class ThreadDomain22 {
        private String userNameParam;
        private String passwordParam;
        private String anyString = new String();
    
        public void setUserNamePassword(String userName, String password)
        {
            try
            {
                synchronized (anyString)
                {
                    System.out.println("线程名称为:" + Thread.currentThread().getName() +
                            "在 " + new Date() + " 进入同步代码块");
                    userNameParam = userName;
                    Thread.sleep(3000);
                    passwordParam = password;
                    System.out.println("线程名称为:" + Thread.currentThread().getName() +
                            "在 " + new Date() + " 离开同步代码块");
                }
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    

    MyThread22_0类

    public class MyThread22_0 extends Thread{
        private ThreadDomain22 td;
    
        public MyThread22_0(ThreadDomain22 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.setUserNamePassword("A", "AA");
        }
    }
    

    MyThread22_1类

    public class MyThread22_1 extends Thread{
        private ThreadDomain22 td;
    
        public MyThread22_1(ThreadDomain22 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.setUserNamePassword("B", "B");
        }
    }
    

    main

    public class MyThread22_main{
        public static void main(String[] args)
        {
            ThreadDomain22 td = new ThreadDomain22();
            MyThread22_0 mt0 = new MyThread22_0(td);
            MyThread22_1 mt1 = new MyThread22_1(td);
            mt0.start();
            mt1.start();
        }
    }
    

    输出结果

    线程名称为:Thread-0在 Tue Jul 02 14:51:16 CST 2019 进入同步代码块
    线程名称为:Thread-0在 Tue Jul 02 14:51:19 CST 2019 离开同步代码块
    线程名称为:Thread-1在 Tue Jul 02 14:51:19 CST 2019 进入同步代码块
    线程名称为:Thread-1在 Tue Jul 02 14:51:22 CST 2019 离开同步代码块
    

    synchronized(anyString)和synchronized(this)效果一致,anyString作为全局变量是一个对象,如果放入try里面,就变成了多个对象,变成了异步执行。

    再看一个例子
    ThreadDomain23类

    public class ThreadDomain23 {
        public void testMethod1(MyObject mo)
        {
            try
            {
                synchronized (mo)
                {
                    System.out.println("testMethod1__getLock time = " +
                            new Date() + ", run ThreadName = " +
                            Thread.currentThread().getName());
                    Thread.sleep(5000);
                    System.out.println("testMethod1__releaseLock time = " +
                            new Date() + ", run ThreadName = " +
                            Thread.currentThread().getName());
                }
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    

    MyObject类

    public class MyObject {
        public synchronized void speedPrintString()
        {
            System.out.println("speedPrintString__getLock time = " +
                    new Date() + ", run ThreadName = " +
                    Thread.currentThread().getName());
            System.out.println("speedPrintString__releaseLock time = " +
                    new Date() + ", run ThreadName = " +
                    Thread.currentThread().getName());
        }
    }
    

    MyThread23_0类

    public class MyThread23_0 extends Thread{
        private ThreadDomain23 td;
        private MyObject mo;
    
        public MyThread23_0(ThreadDomain23 td, MyObject mo)
        {
            this.td = td;
            this.mo = mo;
        }
    
        public void run()
        {
            td.testMethod1(mo);
        }
    }
    

    MyThread23_1类

    public class MyThread23_1 extends Thread {
        private MyObject mo;
    
        public MyThread23_1(MyObject mo)
        {
            this.mo = mo;
        }
    
        public void run()
        {
            mo.speedPrintString();
        }
    }
    

    main方法

    public class MyThread23_main {
        public static void main(String[] args)
        {
            ThreadDomain23 td = new ThreadDomain23();
            MyObject mo = new MyObject();
            MyThread23_0 mt0 = new MyThread23_0(td, mo);
            MyThread23_1 mt1 = new MyThread23_1(mo);
            mt0.start();
            mt1.start();
        }
    }
    

    输出结果如下

    testMethod1__getLock time = Tue Jul 02 15:23:05 CST 2019, run ThreadName = Thread-0
    testMethod1__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-0
    speedPrintString__getLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1
    speedPrintString__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1
    

    speedPrintString方法总是在testMethod1方法之后执行,说明ThreadDomain23获得了锁,mo对象中的syncronized方法被阻塞,执行完testMethod1方法才会执行speedPrintString方法。
    即当一个线程调用该方法时,其他线程阻塞。
    结论:
    1.当多个线程同时执行synchronized(非this对象){}同步代码块时呈同步效果
    2.当其他线程执行非this对象中的synchronized同步方法时呈同步效果

    synchronized静态方法

    synchronized static可以对当前的类加锁,即类锁。
    ThreadDomain24类

    public class ThreadDomain24 {
        public synchronized static void printA()
        {
            try
            {
                System.out.println("线程名称为:" + Thread.currentThread().getName() +
                        "在" + System.currentTimeMillis() + "进入printA()方法");
                Thread.sleep(3000);
                System.out.println("线程名称为:" + Thread.currentThread().getName() +
                        "在" + System.currentTimeMillis() + "离开printA()方法");
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    
        public synchronized static void printB()
        {
            System.out.println("线程名称为:" + Thread.currentThread().getName() +
                    "在" + System.currentTimeMillis() + "进入printB()方法");
            System.out.println("线程名称为:" + Thread.currentThread().getName() +
                    "在" + System.currentTimeMillis() + "离开printB()方法");
    
        }
    
        public synchronized void printC()
        {
            System.out.println("线程名称为:" + Thread.currentThread().getName() +
                    "在" + System.currentTimeMillis() + "进入printC()方法");
            System.out.println("线程名称为:" + Thread.currentThread().getName() +
                    "在" + System.currentTimeMillis() + "离开printC()方法");
        }
    }
    

    MyThread24_0类,静态方法

    public class MyThread24_0 extends Thread{
        public void run()
        {
            ThreadDomain24.printA();
        }
    }
    

    MyThread24_1类,静态方法

    public class MyThread24_1 extends Thread {
        public void run()
        {
            ThreadDomain24.printB();
        }
    }
    

    MyThread24_2类,实例化调用方法

    public class MyThread24_2 extends Thread{
        private ThreadDomain24 td;
    
        public MyThread24_2(ThreadDomain24 td)
        {
            this.td = td;
        }
    
        public void run()
        {
            td.printC();
        }
    }
    

    main方法

    public class MyThread24_main {
        public static void main(String[] args)
        {
            ThreadDomain24 td = new ThreadDomain24();
            MyThread24_0 mt0 = new MyThread24_0();
            MyThread24_1 mt1 = new MyThread24_1();
            MyThread24_2 mt2 = new MyThread24_2(td);
            mt0.start();
            mt1.start();
            mt2.start();
        }
    }
    

    输出结果如下

    线程名称为:Thread-0在1562057924755进入printA()方法
    线程名称为:Thread-2在1562057924755进入printC()方法
    线程名称为:Thread-2在1562057924755离开printC()方法
    线程名称为:Thread-0在1562057927755离开printA()方法
    线程名称为:Thread-1在1562057927755进入printB()方法
    线程名称为:Thread-1在1562057927755离开printB()方法
    

    可以看到,printA方法(类锁)和printB方法(类锁)同步执行,printC方法(对象锁)异步执行,printA方法和printB方法互斥,printC不受类锁影响。
    结论:
    静态方法持有类锁,非静态方法持有对象锁,两者互不干扰。

  • 相关阅读:
    36、基于TCP、UDP协议的嵌套字通信
    34、异常以及网络编程
    作业4月15号
    31、反射与内置方法、元类
    30、多态与鸭子类型以及内置函数
    作业4月9号
    29、继承
    作业4月8号
    28、封装
    27、面向对象
  • 原文地址:https://www.cnblogs.com/Java-Starter/p/11088977.html
Copyright © 2011-2022 走看看