java中的synchronized同步锁

在Java代码中,多线程的环境下很多时候需要用到synchronized来控制线程同步的。

synchronized是Java中的关键字,是一种同步锁。它的用法主要分为一下几种:

1. 修饰一个普通方法:

     被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的this对象;

测试:

public class BaseBean {

    /**
     * synchronized修饰普通方法
     */
    public synchronized void  test(String name){
        System.out.println(name + ",普通test方法:进入锁");
        try {
            Thread.currentThread().sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + ",普通test方法:离开锁");
    }

    /**
     * synchronized修饰普通方法
     */
    public synchronized void putongtest(String name){
        System.out.println(name + ",普通putongtest方法:进入");
        try {
            Thread.currentThread().sleep(3*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + ",普通putongtest方法:离开");
    }out.println(name + ",普通方法:离开");
    }
}

测试方法:

public static void main(String[] args ){
        BaseBean baseBean = new BaseBean();
        Thread thread1 = new Thread("线程1"){
            @Override
            public void run() {
                baseBean.test("线程1");
            }
        };

        BaseBean baseBean1 = new BaseBean();
        Thread thread2 = new Thread("线程2"){
            @Override
            public void run() {
                baseBean1.test("线程2");
                 //baseBean1.test("线程2");
                //baseBean.putongtest("线程2");
            }
        };

        thread1.start();
        thread2.start();
    }

首先测试不同线程同一个对象调用同一个同步方法;再测试不同线程不同对象调用同一个同步方法;最后再测试不同线程同一个对象调用不同的同步方法。得到测试结果如下:

线程同一个对象调用同一个同步方法。
    线程1,普通test方法:进入锁
    线程1,普通test方法:离开锁
    线程2,普通test方法:进入锁
    线程2,普通test方法:离开锁

不同线程不同对象调用同一个同步方法。
    线程1,普通test方法:进入锁
    线程2,普通test方法:进入锁
    线程1,普通test方法:离开锁
    线程2,普通test方法:离开锁

不同线程同一个对象调用不同的同步方法
    线程1,普通test方法:进入锁
    线程1,普通test方法:离开锁
    线程2,普通putongtest方法:进入
    线程2,普通putongtest方法:离开

可以得到结论:同一个对象的所有synchronized修饰的普通方法都公用一个this对象。这些方法再多线程环境中是线程串行地执行,同一时间只能运行一个线程,其他线程处于等待状态。当我们多次new不同的对象时,每个方法的this对象不一致。可并行运行。

2. 修饰代码块

          被修饰的代码块称为同步代码块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象this;synchronized修饰普通方法和synchronized修饰this代码块的处理方式其实是一致的。

/**
     * synchronized修饰代码块,对象是this
     */
    public void test2(String name){

        synchronized(this){
            System.out.println(name + ",synchronized修饰test2代码块,对象是this:进入锁");
            try {
                Thread.currentThread().sleep(3 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",synchronized修饰test2代码块:离开锁");
        }
    }
    /**
     * synchronized修饰代码块,对象是this
     */
    public void putongtest2(String name){
        synchronized(this){
            System.out.println(name + ",synchronized修饰putongtest2代码块,对象是this:进入锁");
            try {
                Thread.currentThread().sleep(3 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",synchronized修饰putongtest2代码块:离开锁");
        }
    }

 这里就不展示测试结果了。synchronized修饰普通方法和synchronized修饰this代码块的处理方式其实是一致的。

在某些时候synchronized(obj)修饰的代码块,而且obj自己的方法中包含了 synchronized(this)代码块或者synchronized修饰的普通方法。那么这些代码公用一个obj对象锁,这些代码都是同步的。

测试方法如下;

构建一个类里面有不同的synchronized方法和代码块。

public class TestObject {
    synchronized public void test1(){
        System.out.println("TestObject-test1方法执行");
        try {
            Thread.currentThread().sleep(10* 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("TestObject-test1方法结束");
    }
    synchronized public void test2(){
        System.out.println("TestObject-test2方法执行");
        try {
            Thread.currentThread().sleep(10* 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("TestObject-test2方法结束");
    }

    public void test3(){
        synchronized (this) {
            System.out.println("TestObject-test3方法执行");
            try {
                Thread.currentThread().sleep(10* 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("TestObject-test3方法结束");
        }
    }
}

在一个线程中分别执行这些方法。

public class TestThread extends Thread {
    TestObject testObject;

    String msg;

    public TestThread(TestObject testObject, String msg){
        this.testObject = testObject;
        this.msg = msg;
    }

    @Override
    public void run(){
        try {
            if ("1".equals(msg)) {
                testObject.test1();
            } else if ("2".equals(msg)) {
                testObject.test2();
            } else if ("3".equals(msg)) {
                testObject.test3();
            } else {
                synchronized (testObject) {
                    System.out.println("TestThread方法执行");
                    try {
                        Thread.currentThread().sleep(10* 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("TestThread方法结束");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 多线程进行测试:

public static void main(String[] args){
        TestObject testObject = new TestObject();
        TestThread testThread = new TestThread(testObject, "1");
        TestThread testThread1 = new TestThread(testObject, "2");
        TestThread testThread2 = new TestThread(testObject, "3");
        TestThread testThread3 = new TestThread(testObject, "4");
        testThread.start();
        testThread1.start();
        testThread2.start();
        testThread3.start();
    }

测试结果如下:

TestThread方法执行
TestThread方法结束
TestObject-test1方法执行
TestObject-test1方法结束
TestObject-test2方法执行
TestObject-test2方法结束
TestObject-test3方法执行
TestObject-test3方法结束

可以看到这些方法和代码块都是同步的。 

3. 修改一个静态的方法:

         其作用的范围是整个静态方法,作用的对象是这个类的所有对象,也就是class对象。

/**
     * synchronized修饰静态方法
     */
    public synchronized static void staticTest(String name){
        System.out.println(name + ",静态方法staticTest:进入锁");
        try {
            Thread.currentThread().sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + ",普通方法staticTest:离开锁");
    }

    /**
     * synchronized修饰静态方法
     */
    public synchronized static void staticTest2(String name){
        System.out.println(name + ",静态方法staticTest2:进入锁");
        try {
            Thread.currentThread().sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + ",普通方法staticTest2:离开锁");
    }

测试方式:

public static void main(String[] args ){
        BaseBean baseBean = new BaseBean();
        Thread thread1 = new Thread("线程1"){
            @Override
            public void run() {
                baseBean.staticTest("线程1");
            }
        };

        BaseBean baseBean1 = new BaseBean();
        Thread thread2 = new Thread("线程2"){
            @Override
            public void run() {
                //baseBean.staticTest2("线程2");
                //baseBean1.staticTest2("线程2");
                baseBean.staticTest2("线程2");
            }
        };

        thread1.start();
        thread2.start();
    }

       首先测试不同线程同一个对象调用同一个同步方法;再测试不同线程不同对象调用同一个同步方法;最后再测试不同线程同一个对象调用不同的同步方法。得到测试结果如下:

测试 不同线程 同一个对象 调用 同一个同步代码块:
线程2,静态方法staticTest:进入锁
线程2,普通方法staticTest:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁

测试 不同线程 同一个对象 调用 不同的同步代码块:
线程2,静态方法staticTest:进入锁
线程2,普通方法staticTest:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁


测试 不同线程 不同对象 调用 不同的同步代码块:
线程2,静态方法staticTest2:进入锁
线程2,普通方法staticTest2:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁

测试 不同线程 同一个对象调用 不同的同步代码块:
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁
线程2,静态方法staticTest2:进入锁
线程2,普通方法staticTest2:离开锁

可以得到结论:同一个bean的所有对象的所有synchronized修饰的静态方法都公用一个class对象。这些方法再多线程环境中是线程串行地执行,同一时间只能运行一个线程,其他线程处于等待状态。当我们多次new不同的对象时,每个方法的class对象不变。同一时间只能运行一个线程,其他线程处于等待状态。

4.  修改一个类(class)/静态成员变量:

      作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象,即class对象。这个结果和synchronized修饰静态方法是同样的结果,这里就不再重复测试了。

synchronized修饰的代码中内层抛出异常,跳出synchronized代码块,会释放锁。所以其他的线程可以继续执行synchronized修饰的代码。

synchronized虽然是用来来定义方法,但synchronized并不属于方法定义的一部分。因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中如果重写方法,在子类中的这个方法如果需要同步就必须加上synchronized关键字才可以。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值