Synchronized使用方式

Synchronized使用方式

demo1

package com.w.juc;

public class SynchronizedDemo {

    static int value;


    public static void increment() {
        for (int i = 0; i <10000; i++) {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> increment(), "线程【A】");
        Thread thread1 = new Thread(() -> increment(), "线程【B】");
        thread.start();
        thread1.start();
        thread.join();//等待线程A执行完毕后返回
        thread1.join();
        System.out.println(value);//这里正常想要打印出来2万
    }
}

上面代码并没有用到synchronized,也就是说并未同步。先说明上面代码是一个有着并发问题的代码:value是一个共享变量,而value++其实是三步操作,先读取,后+1,最后写入。对于A,B线程来说,可能出现如下场景,线程A读取了value=4,在未+1时,线程B也读取到了value=4。这时线程A执行完毕value=5,但是对于线程B执行后的结果也是value=5,这就少加了一次。

可见,多个线程共同访问修改一个共享变量会出现并发问题,所以需要解决,这里就使用到了synchronized,当然并发包下的也可以用。

synchronized实现同步的三种方式。

  • 可以锁普通方法
  • 锁静态方法
  • 代码块

demo2

package com.w.juc;

public class SynchronizedDemo {

    static int value;


    public static synchronized void increment() {
        for (int i = 0; i <10000; i++) {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> increment(), "线程【A】");
        Thread thread1 = new Thread(() -> increment(), "线程【B】");
        thread.start();
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println(value);//一定输出2万
    }
}

先使用的是在静态方法上使用。锁的是当前Class对象,类对象。或者

demo3

package com.w.juc;

public class SynchronizedDemo {

    static int value;


    public  synchronized void increment() {
        for (int i = 0; i <10000; i++) {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo sd = new SynchronizedDemo();
        Thread thread = new Thread(sd::increment, "线程【A】");//Java8特性,方法引用
        Thread thread1 = new Thread(() -> sd.increment(), "线程【B】");//Lambda表达式
        thread.start();
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println(value);
    }
}

demo3锁的是对象实例,也就是sd。现在demo3锁的是Class对象,对于demo2和demo3来说好像没什么区别都是正常输出2万

但是现在看看demo4

package com.w.juc;

public class SynchronizedDemo {

    static int value;


    public  synchronized void increment() {
        for (int i = 0; i <10000; i++) {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo sd = new SynchronizedDemo();
        SynchronizedDemo sd2 = new SynchronizedDemo();//和demo3区别
        Thread thread = new Thread(sd::increment, "线程【A】");
        Thread thread1 = new Thread(() -> sd2.increment(), "线程【B】");//区别
        thread.start();
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println(value);
    }
}

对于普通方法上加锁,锁的是当前实例对象,也就是demo4中sd和sd2这是俩对象,所以线程A持有锁sd,线程B持有锁sd1,这样大家互不干扰,也就都可以同时访问修改value和deom1一样的并发问题出现了。怎么解决呢?看看demo2,锁的是类对象,而类对象只存在一个。看demo5吧

demo5

package com.w.juc;

public class SynchronizedDemo {

    static int value;


    public static synchronized void increment() {//加static,锁的就是类对象
        for (int i = 0; i <10000; i++) {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo sd = new SynchronizedDemo();
        SynchronizedDemo sd2 = new SynchronizedDemo();
        Thread thread = new Thread(()->sd.increment(), "线程【A】");//这里我的会报错。。。因为加了阿里巴巴规范插件,不推荐我使用对象实例调用类的静态成员
        Thread thread1 = new Thread(() -> sd2.increment(), "线程【B】");
        thread.start();
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println(value);//正常输出2万
    }
}

锁了类对象,俩实例对象sd和sd2就会独占的获取锁,同一时刻只有一个线程才能进入increment方法

以问题深入记忆三种使用方式

可以看视频

问题1:标志访问先打印哪个?

package com.w.juc;

class Person{
    public synchronized void eatFood(){
        System.out.println("吃肉");
    }

    public synchronized void drinkWater(){
        System.out.println("喝水");
    }
}

public class SynchronizedDemo {
    
    public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        new Thread(()->person.eatFood(),"t1").start();
        new Thread(()->person.drinkWater(),"t2").start();
    }
}
//吃肉
//喝水

解释:对普通方法使用synchronized锁的是当前实例对象person,当线程t1启动则获取到锁,线程t2必须等待t1执行结束后执行

问题2:吃饭用2s,先打印哪个?

package com.w.juc;

class Person{
    public synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public synchronized void drinkWater(){
        System.out.println("喝水");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        new Thread(()->person.eatFood(),"t1").start();
        new Thread(()->person.drinkWater(),"t2").start();
    }
}
//吃肉
//喝水

解释:和问题1一样
问题3:新增普通方法sleep,先打印哪个?

package com.w.juc;

class Person{
    public synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public synchronized void drinkWater(){
        System.out.println("喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        new Thread(()->person.eatFood(),"t1").start();
        new Thread(()->person.toSleep(),"t2").start();
    }
}
睡觉
吃肉

解释:睡觉是个普通未同步方法,随时可以访问,只要线程t2不去抢实例对象person锁,则不会被阻塞。

问题4:俩人,先吃肉还是喝水?

package com.w.juc;

class Person{
    public synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public synchronized void drinkWater(){
        System.out.println("喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();
        new Thread(()->person2.drinkWater(),"t2").start();
    }
}
喝水
吃肉

解释:俩人只要不去干同一件事就不会有冲突,锁的是当前实例对象,线程t1和线程t2持有的锁不一样,就不会冲突,也就不存在因为锁阻塞线程

问题5:静态同步方法,一个人,先吃肉还是喝水?

package com.w.juc;

class Person{
    public static synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public static synchronized void drinkWater(){
        System.out.println("喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();
        new Thread(()->person1.drinkWater(),"t2").start();
    }
}
吃肉
喝水

解释:在静态方法上面用synchroniezd,锁的是当前类对象,只有一个类对象。所以各个线程还是需要等待锁的获取

package com.w.juc;

class Person{
    public static synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public static synchronized void drinkWater(){
        System.out.println(Thread.currentThread()+"喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();

        new Thread(()->person1.drinkWater(),"t2").start();
        new Thread(()->person1.drinkWater(),"t1").start();
    }
}
吃肉
Thread[t1,5,main]喝水
Thread[t2,5,main]喝水

这里按照程序顺序应该是线程t2先喝水,为什么执行却是线程t1先喝?因为重进入,虽然线程t1和t2喝水都被阻塞了,但是t1先持有锁,可重进入锁就快

问题6:静态同步方法,俩人,先打印吃肉还是喝水?

package com.w.juc;

class Person{
    public static synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public static synchronized void drinkWater(){
        System.out.println(Thread.currentThread()+"喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();

        new Thread(()->person2.drinkWater(),"t2").start();

    }
}
吃肉
Thread[t2,5,main]喝水

解释:和问题5一样,静态方法,获取到的锁是类对象,只有一个,也就是说同时只有一个线程可以获取到锁,去执行方法。

问题7:一个静态同步方法,一个普通方法,一个人,会先打印什么

package com.w.juc;

class Person{
    public static synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public static synchronized void drinkWater(){
        System.out.println(Thread.currentThread()+"喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();

        new Thread(()->person1.toSleep(),"t2").start();

    }
}
睡觉
吃肉

解释: 普通方法可以任意访问,不受限制

问题8:一个静态同步方法,一个普通方法,俩个人,会先打印什么

package com.w.juc;

class Person{
    public static synchronized void eatFood(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("吃肉");
    }

    public static synchronized void drinkWater(){
        System.out.println(Thread.currentThread()+"喝水");
    }

    public void toSleep(){
        System.out.println("睡觉");
    }
}

public class SynchronizedDemo {

    public static void main(String[] args) throws InterruptedException {
        Person person1 = new Person();
        Person person2 = new Person();
        new Thread(()->person1.eatFood(),"t1").start();

        new Thread(()->person2.toSleep(),"t2").start();

    }
}
睡觉
吃肉

解释:和问题7一样,没有同步的方法并不会是独占的,任意线程都可以访问。

以上只是写了怎么使用和一些理解。主要还得深究原理,和JVM相关,对象头。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值