多线程系列四-同步-Synchronized

synchronized 介绍

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修饰类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

非线程安全

多个线程共同访问一个对象中的实例变量,才会出现“非线程安全”问题。

锁相关

  1. 多个对象对应多个锁。一个对象只有一个锁。
  2. 每次访问对修饰的方法,需要获取该对象的锁。所以即使调用的同一个对象不同的同步方法,每次也只能执行其中一个方法。

修饰方法

  1. 场景一:两个线程分为访问同一个实例变量的同步和非同步方法。
public class SynchronizedMethod {

    private static class MyObject {
        public synchronized void methodA() {
            System.out.println("methodA start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodA end");
        }

        public void methodB() {
            System.out.println("methodB start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodB end");
        }
    }

    private static class MythreadA extends Thread {
        private MyObject object;

        MythreadA(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodA();
        }
    }

    private static class MythreadB extends Thread {
        private MyObject object;

        MythreadB(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodB();
        }
    }

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        MythreadA mythread1 = new MythreadA(myObject);
        MythreadB mythread2 = new MythreadB(myObject);

        mythread1.start();
        mythread2.start();
    }
}

输出结果:

methodA start
methodB start
methodA end
methodB end

methodB不需要获得锁,所以可以与同步方法异步调用。

  1. 场景二:两个线程分别访问同一个实例变量的不同的同步方法。
在上一个示例的methodB加上Syncrhoniezed
public class SynchronizedMethod {

    private static class MyObject {
        public synchronized void methodA() {
            System.out.println("methodA start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodA end");
        }

        public synchronized void methodB() {
            System.out.println("methodB start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodB end");
        }
    }

    private static class MythreadA extends Thread {
        private MyObject object;

        MythreadA(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodA();
        }
    }

    private static class MythreadB extends Thread {
        private MyObject object;

        MythreadB(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodB();
        }
    }

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        MythreadA mythread1 = new MythreadA(myObject);
        MythreadB mythread2 = new MythreadB(myObject);

        mythread1.start();
        mythread2.start();
    }
}

输出结果:
methodA start
methodA end
methodB start
methodB end

他们是虽然是两个方法,但是因为是同一个实例(每次只能有一个线程获得对象的锁),所以也是顺序调用的。

修改代码块

当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

场景一: 两个线程分别访问同步方法和包含同步代码块的方法

public class SynchronizedBlock {

    private static class MyObject {
        public synchronized void methodA() {
            System.out.println("methodA start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodA end");
        }

        public void methodB() {

            System.out.println("methodB start 1"); //这个不在同步方法内可以先执行
            synchronized (this) {
                System.out.println("methodB start 2");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("methodB end");
            }
        }
    }

    private static class MythreadA extends Thread {
        private MyObject object;

        MythreadA(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodA();
        }
    }

    private static class MythreadB extends Thread {
        private MyObject object;

        MythreadB(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodB();
        }
    }

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        MythreadA mythread1 = new MythreadA(myObject);
        MythreadB mythread2 = new MythreadB(myObject);

        mythread1.start();
        mythread2.start();
    }
}

methodB方法

System.out.println("methodB start 1"); //这个不在同步方法内可以先执行

这块代码不在任何同步方法或者代码块中,可以不同等待直接运行。

  • synchronized锁重入
    即线程访问同步方法methodA获得锁后,统一可以访问另一个同步方法methodB
  • 出现异常时,会自动释放锁

- 同步具有继承性:重写父类同步方法时,子类方法若没有用synchronized修饰,也是一个非同步方法

Synchronized原理

了解Synchronized原理,对于上面的一些场景就一目了然了。
详细解释可以参考这里
每个对象有一个监视器锁monitor,每次进入Synchronized方法时,monitor相当于被占用处于锁定状态。monitor默认是0,线程进入后为1。

和static一起及Synchronize(class)

  • 用在static方法上时,是对当前.javaw文件对哦应的Class类进行持锁(暂且称为类锁,前面实例变量锁暂称为对象锁)。
  • 类锁和对象锁是不同的一种锁
    实验实例:
public class SynchronizedStatic {

    private static class MyObject {
        public synchronized void methodA() {
            System.out.println("methodA start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodA end");
        }

        public  static synchronized void methodB() {
                System.out.println("methodB start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               System.out.println("methodB end");

        }
    }

    private static class MythreadA extends Thread {
        private MyObject object;

        MythreadA(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodA();
        }
    }

    private static class MythreadB extends Thread {
        private MyObject object;

        MythreadB(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodB();
        }
    }

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        MythreadA mythread1 = new MythreadA(myObject);
        MythreadB mythread2 = new MythreadB(myObject);

        mythread1.start();
        mythread2.start();
    }
}

结果:

methodA start
methodB start 2
methodA end
methodB end
  • 类锁对所有实例对象有效
    代码示例:
public class SynchronizedStatic2 {

    private static class MyObject {
        public static synchronized void methodA() {
            System.out.println("methodA start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("methodA end");
        }

        public  static synchronized void methodB() {
                System.out.println("methodB start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               System.out.println("methodB end");

        }
    }

    private static class MythreadA extends Thread {
        private MyObject object;

        MythreadA(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodA();
        }
    }

    private static class MythreadB extends Thread {
        private MyObject object;

        MythreadB(MyObject object) {
            this.object = object;
        }

        @Override
        public void run() {
            object.methodB();
        }
    }

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        MyObject myObject2 = new MyObject();//这里创建两个实例
        MythreadA mythread1 = new MythreadA(myObject);
        MythreadB mythread2 = new MythreadB(myObject2);

        mythread1.start();
        mythread2.start();
    }
}

结果:
methodA start
methodA end
methodB start
methodB end
  • 注意使用String的常量池特性。
String a="aa";
String b="aa";
//这里a和b持有的同一个对象锁
  • 在static方法中,用Synchronized(class)作用和Synchronized static作用一样

参考

http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html
https://www.cnblogs.com/paddix/p/5367116.html
https://www.cnblogs.com/paddix/p/5367116.html

你可能感兴趣的:(java-多线程并发)