JUC:5锁的种类:synchronized修饰方法锁为例

1.同一把锁:synchronized修饰的普通方法

  1. 标准情况下,两个线程先打印funcA,再funcB:谁先获得锁,谁先输出
  2. funA不释放锁的情况下,先打印funcA,再funcB:谁获得锁,谁输出
  3. 增加一个普通方法(非同步方法),不受锁的影响,正常cpu调度到了输出
package juc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * synchronized修饰的方法,锁的对象是方法的调用者
 */
public class Test1 {

    public static void main(String[] args) {

        ServiceImpl service = new ServiceImpl();
        new Thread(() -> {
            service.funcA();
        }, "A").start();//service就是锁

        try {
            TimeUnit.SECONDS.sleep(1);//睡1s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
//            TimeUnit.SECONDS.sleep(4);//睡4s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            service.funcB();
        }, "B").start();//service就是锁
    }
}

class ServiceImpl {

    /**
     * synchronized修饰的方法,锁的对象是方法的调用者
     */
    public synchronized void funcA() {
        try {
//            Thread.sleep(3000);
            /**
             * 3s之后输出A,因为sleep不会释放锁,是抱着锁睡觉
             * A===funcA
             * B===funcB
             */
            wait(3000);
            /**
             * B===funcB
             * 3s之后输出A,因为wait会释放锁,如果上面TimeUnit.SECONDS.sleep(1),睡眠>wait的时间,比如4,那么依然是A先执行,因为B还没有就绪,不会获得锁
             * A===funcA
             */
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "===funcA");
    }

    public synchronized void funcB() {
        System.out.println(Thread.currentThread().getName() + "===funcB");
    }
}

这里不用wait时,这里A先输出的话,并不是因为A先被调用,因为线程的执行是无序的,之所以A先输出,是因为A先获得了锁(谁先拿到锁谁先输出),而synchronized修饰方法的时候,锁的对象是调用该方法的对象

2.多把锁:synchronized修饰的普通方法

不同的锁,线程获取自己的锁之后,开始执行,互不影响

package juc.lock8;

import org.apache.commons.lang3.time.DateFormatUtils;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * synchronized修饰的方法,锁的对象是方法的调用者
 */
public class Test2 {

    public static void main(String[] args) {

        ServiceImpl2 service = new ServiceImpl2();
        ServiceImpl2 service2 = new ServiceImpl2();
        new Thread(() -> {
            service.funcA();
        }, "A").start();//service就是锁

        try {
            TimeUnit.SECONDS.sleep(1);//睡1s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
//            TimeUnit.SECONDS.sleep(4);//睡4s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            service2.funcB();
        }, "B").start();//service2就是锁,不同的锁,互不影响
    }
    /**
     * 线程获取自己的锁之后,开始执行,互不影响
     * A===funcA获得锁时间20:36:52+08:00
     * B===funcB获得锁时间20:36:53+08:00
     * A===funcA用完锁时间20:36:55+08:00
     */
}

class ServiceImpl2 {

    /**
     * synchronized修饰的方法,锁的对象是方法的调用者
     */
    public synchronized void funcA() {
        System.out.println(Thread.currentThread().getName() + "===funcA获得锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
        try {
//            Thread.sleep(3000);
            wait(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "===funcA用完锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
    }

    public synchronized void funcB() {
        System.out.println(Thread.currentThread().getName() + "===funcB获得锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
    }
}

这里用的时间是apache.lang3的DateFormatUtils,线程安全。
pom依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

3.synchronized修饰的static方法

静态方法属于类对象,锁的是类对象,准确地讲,是这个类的class对象。

详解: static修饰的,类一加载就存在了,意味着Java文件–>class模板的转变,一个Java文件只有唯一对应的一个class文件。
Java获取class对象就是XXX类.class获得对象,class对象是全局唯一的,因此synchronized加修饰的static方法,锁的对象就是这个class对象。

package juc.lock8;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * synchronized+static修饰的方法,锁的是class模板,就是类的class对象
 */
public class Test3 {

    public static void main(String[] args) {

        //不管是几个对象,它的class对象(类模板)都是全局唯一的
        ServiceImpl3 service = new ServiceImpl3();
        ServiceImpl3 service2 = new ServiceImpl3();
        System.out.println(service.getClass().equals(service2.getClass()));//true
        new Thread(() -> {
            service.funcA();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);//睡1s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
//            TimeUnit.SECONDS.sleep(4);//睡4s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            service2.funcB();
        }, "B").start();

        try {
            TimeUnit.SECONDS.sleep(1);//睡1s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
//            TimeUnit.SECONDS.sleep(4);//睡4s,让主线程阻塞,先不执行B的start方法,这样B就会在n秒后进入就绪状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            service2.funcC();
        }, "C").start();

        /**
         * funcA和funB是static synchronized修饰的,锁是类的class对象,全局唯一,同一把锁,因此会竞争,会互相干预,一个完了另一个
         * funC是synchronized修饰的,锁是调用的对象
         * A===funcA获得锁时间21:17:10+08:00
         * C===funcC获得锁时间21:17:12+08:00
         * A===funcA用完锁时间21:17:15+08:00
         * B===funcB获得锁时间21:17:15+08:00
         */
    }

}

class ServiceImpl3 {

    /**
     * synchronized修饰的方法,锁的对象是方法的调用者
     */
    public static synchronized void funcA() {
        System.out.println(Thread.currentThread().getName() + "===funcA获得锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
        try {
            Thread.sleep(5000);
//            wait(3000);//wait属于object方法,不能用于static方法中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "===funcA用完锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
    }

    public static synchronized void funcB() {
        System.out.println(Thread.currentThread().getName() + "===funcB获得锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
    }

    public synchronized void funcC() {
        System.out.println(Thread.currentThread().getName() + "===funcC获得锁时间" + DateFormatUtils.ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.format(new Date()));
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值