每天学习一点点211129/211130--synchronized关键字

内部锁:synchronized 关键字

  Java 中每个对象都有一个与之关联的内部锁(intrinsic lock)。这种锁也称为监视器(Monitor),这种内部锁是一种排他锁,可以保障原子性,可见性与有序性。
  内部锁是通过 synchronized 关键字实现的。synchronized 关键字修饰代码块,修饰方法。修饰实例方法就称为同步实例方法,修饰静态方法就称为同步静态方法。
  修饰代码块的语法:

synchronized(对象锁){
	同步代码块,可以在同步代码块中访问共享数据
}
  1. 同步代码块
    使用this当前对象作为锁对象
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步代码块
 * @author Jason
 */
public class Test01 {
    public static void main(String[] args) {
        //创建两个线程,分别调用mm()方法
        Test01 obj = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象this就是obj对象
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm();//使用的锁对象this也是obj对象
            }
        }).start();
    }
    //定义方法,打印100行字符串
    public void mm(){
        synchronized (this) {//经常使用this当前对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }
}

输出结果:Thread-0先输出0-100,Thread-1后输出


如果线程的锁不同,则不能实现同步

package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步代码块
 * 如果线程的锁不同,则不能实现同步
 * 想要同步必须使用用一个锁对象
 *
 * @author Jason
 */
public class Test02 {
    public static void main(String[] args) {
        //创建两个线程,分别调用mm()方法
        Test02 obj = new Test02();
        Test02 obj2 = new Test02();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象this就是obj对象
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj2.mm();//使用的锁对象this是 obj2 对象
            }
        }).start();
    }

    //定义方法,打印100行字符串
    public void mm() {
        synchronized (this) {//经常使用this当前对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }
}

输出结果:Thread-0 和 Thread-1 两个线程交替输出,因为两个线程执行mm()方法并没有使用同一个锁


使用一个常量对象作为锁对象

package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步代码块
 * 使用一个常量对象作为锁对象
 * @author Jason
 */
public class Test03 {
    public static void main(String[] args) {
        //创建两个线程,分别调用mm()方法
        Test03 obj = new Test03();
        Test03 obj2 = new Test03();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象this就是obj对象
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj2.mm();//使用的锁对象this是 obj2 对象
            }
        }).start();
    }

    public static final Object OBJ = new Object();
    //定义方法,打印100行字符串
    public void mm() {
        synchronized (OBJ) {//使用一个常量对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }
}


使用一个常量对象作为锁对象,不同方法中的代码也可以同步
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步代码块
 * 使用一个常量对象作为锁对象,不同方法中的代码也可以同步
 *
 * @author Jason
 */
public class Test04 {
    public static void main(String[] args) {
        Test04 obj = new Test04();
        Test04 obj2 = new Test04();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象this就是obj对象
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj2.mm();//使用的锁对象this是 obj2 对象
            }
        }).start();

        //第三个线程调用静态方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                sm();
            }
        });
    }

    //定义一个常量
    public static final Object OBJ = new Object();

    //定义方法,打印100行字符串
    public void mm() {
        synchronized (OBJ) {//使用一个常量对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

    public static void sm(){
        synchronized (OBJ) {//使用一个常量对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }
}

  1. 同步方法

同步实例方法,把整个方法作为同步代码块,默认的锁对象是 this 对象

package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步实例方法
 * 把整个方法作为同步代码块,默认的锁对象是 this 对象
 * @author Jason
 */
public class Test05 {
    public static void main(String[] args) {
        //创建 Test05对象,通过对象名调用 mm()方法
        Test05 obj = new Test05();

        //一个线程调用 mm()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象this就是obj对象
            }
        }).start();
        //另一个线程调用mm22()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm22();//使用的锁对象this是 obj2 对象

            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //使用的锁对象this是 new 创建的新对象,不是一个锁对象不能同步
                new Test05().mm22();
            }
        }).start();
    }

    //定义方法,打印100行字符串
    public void mm() {
        synchronized (this) {//使用this当前对象作为锁对象
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

    //使用 synchronized 修饰实例方法,同步实例方法,默认 this 作为锁对象
    public synchronized void mm22(){
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}


同步静态方法,默认的锁对象是当前类的运行时类对象
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * synchronized 同步静态方法
 * 把整个方法作为同步代码块,默认的锁对象是当前类的运行时类对象
 * @author Jason
 */
public class Test06 {
    public static void main(String[] args) {
        Test06 obj = new Test06();

        //一个线程调用 mm()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象是 Test06.class
            }
        }).start();
        //另一个线程调用mm22()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                Test06.mm22();//使用的锁对象是Test06.class

            }
        }).start();
    }

    //定义方法,打印100行字符串
    public void mm() {
        //使用当前类的运行时类对象作为锁对象,可以简单的理解为把 Test06 类的字节码文件作为锁对象
        synchronized (Test06.class) {
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

    //使用 synchronized 修饰静态方法,同步静态方法,默认运行时类 Test06.class 作为锁对象
    public synchronized static void mm22(){
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}


同步方法,执行效率低。不需要同步的操作代码也被迫成为同步操作 同步代码块,锁的粒度细,执行效率高,可以只同步需要同步操作的地方
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * @author Jason
 * 同步方法锁的粒度粗,执行效率低,同步代码块执行效率高
 */
public class Test07 {
    public static void main(String[] args) {
        Test07 obj = new Test07();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.doLongTimeTask2();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.doLongTimeTask2();
            }
        }).start();

    }

    //同步方法,执行效率低。不需要同步的操作代码也被迫成为同步操作
    public synchronized void doLongTimeTask(){
        try {
            System.out.println("Task Begin");
            Thread.sleep(3000);//模拟任务需要准备 3 秒 钟
            System.out.println("开始同步");
            for (int i = 0; i <= 10000; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
            System.out.println("Task End");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //同步代码块,锁的粒度细,执行效率高,可以只同步需要同步操作的地方
    public void doLongTimeTask2(){
        try {
            System.out.println("Task Begin");
            Thread.sleep(3000);//模拟任务需要准备 3 秒 钟
            System.out.println("开始同步");
            synchronized (this){
                for (int i = 0; i <= 10000; i++) {
                    System.out.println(Thread.currentThread().getName() + "--" + i);
                }
            }
            System.out.println("Task End");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  1. 脏读
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * 脏读
 * 出现读取属性值出现了一些意外,读取的是中间值,而不是修改之后的值
 * 出现脏读的原因是 对共享数据的修改 与对共享数据的读取不同步
 * 解决方法 不仅对修改数据的代码进行同步,还要对读取数据的代码块同步
 *
 * @author Jason
 */
public class Test08 {
    public static void main(String[] args) throws InterruptedException {
        PublicValue publicValue = new PublicValue();
        //开启子线程设置用户名和密码
        SubThread t1 = new SubThread(publicValue);
        t1.start();
        //为了确定设置成功
        //如果 不 sleep 一下,main线程先执行,获取的就是旧值
        Thread.sleep(100);
        //在 main 线程中读取用户名,密码
        //如果 getValue 不是同步方法,读取到的值可能是旧值,因为get值和set值没有同步
        publicValue.getValue();
    }

    static class SubThread extends Thread {
        private PublicValue publicValue;

        public SubThread(PublicValue publicValue) {
            this.publicValue = publicValue;
        }

        @Override
        public void run() {
            publicValue.setValue("ZDJ", "888");
        }
    }

    static class PublicValue {
        private String name = "jason";
        private String pwd = "666";

        public synchronized void getValue() {
            System.out.println(Thread.currentThread().getName() + ", getter -- name: " + name + ",--pwd: " + pwd);
        }

        public synchronized void setValue(String name, String pwd) {
            this.name = name;
            try {
                Thread.sleep(1000); //模拟操作 name 属性需要一定时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.pwd = pwd;
            System.out.println(Thread.currentThread().getName() + ", setter --name:" + name + ", --pwd: " + pwd);
        }
    }
}

  1. 线程出现异常会自动释放锁
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * 同步过程中线程出现异常,会自动释放锁对象
 * @author Jason
 */
public class Test09 {
    public static void main(String[] args) {
        Test09 obj = new Test09();

        //一个线程调用 mm()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.mm(); //使用的锁对象是 Test09.class
            }
        }).start();
        //另一个线程调用mm22()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                Test09.mm22();//使用的锁对象是Test09.class

            }
        }).start();
    }

    //定义方法,打印100行字符串
    public void mm() {
        //使用当前类的运行时类对象作为锁对象,可以简单的理解为把 Test09 类的字节码文件作为锁对象
        synchronized (Test06.class) {
            for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                if (i == 50) {
                    Integer.parseInt("abc");
                }
            }
        }
    }

    //使用 synchronized 修饰静态方法,同步静态方法,默认运行时类 Test09.class 作为锁对象
    public synchronized static void mm22(){
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

Thread-0 输出到50的时候释放锁,停止输出,开始走 Thread-1

  1. 死锁
    有点没太懂,回头再看看
package com.jason.java.duoxiancheng.intrinsiclock;

/**
 * 死锁
 * 在多线程程序中,同步时可能需要使用多个锁,如果获得锁的顺序不一致,可能会导致死锁
 * 如何避免死锁 当需要获得多个锁的时候,所有线程获得锁的顺序保持一致即可
 *
 * @author Jason
 */
public class Test10 {
    public static void main(String[] args) {
        SubThread t1 = new SubThread();
        t1.setName("a");
        t1.start();

        SubThread t2 = new SubThread();
        t2.setName("b");
        t2.start();
    }

    static class SubThread extends Thread {
        private static final Object lock1 = new Object();
        private static final Object lock2 = new Object();

        @Override
        public void run() {
            if ("a".equals(Thread.currentThread().getName())) {
                synchronized (lock1) {
                    System.out.println("a 线程获得了 lock1 锁,还需 要获得 lock2 锁");
                    synchronized (lock2) {
                        System.out.println("a 线程获得 lock1 后又 获得了 lock2,可以想干任何想干的事");
                    }
                }
            }
            if ("b".equals(Thread.currentThread().getName())) {
                synchronized (lock2) {
                    System.out.println("a 线程获得了 lock2 锁,还需 要获得 lock1 锁");
                    synchronized (lock1) {
                        System.out.println("a 线程获得 lock2 后又 获得了 lock1,可以想干任何想干的事");
                    }
                }
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值