sychronized(1)-多线程安全-并发编程(Java)

本文探讨了如何使用synchronized关键字解决多线程中的临界区竞态条件,通过实例展示了synchronized在成员方法和静态方法上的应用,以及其锁对象的原理。分析了不同场景下锁的对象,包括成员方法锁定this,静态方法锁定类对象,并讨论了多个线程对同一对象或不同对象的同步效果。
摘要由CSDN通过智能技术生成

1、应用之互斥

为了避免临界区竞态条件的发生,有多种手段可以达到目的

  • 阻塞式:synchronized,Lock
    • synchronized:Java关键字
    • Lock(锁)为接口,有多个实现类
  • 非阻塞式:非原子变量

这里我们使用阻塞式的解决方案:synchronized,来解决多线程安全问题,既常说的锁对象。它使用互斥的方式保证同一时刻最多只有一个线程能持有对象锁,其他线程想要获取这个对象锁时会阻塞住。这样能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心上下文切换。

2、synchronized简单应用

  • 创建简单计数器类

    • 成员变量:count,初始值0

    • 成员方法:自增、自减、读取count的值

    • 代码:

      public class Counter {
          private int count = 0;
      
          public synchronized void increment() {
              count++;
          }
      
          public synchronized void decrement() {
              count--;
          }
      
          public synchronized int getCount() {
              return count;
          }
      }
      
  • 测试类:

    @Slf4j(topic = "security.syn.TestCounter")
    public class TestCounter {
        public static void main(String[] args) throws InterruptedException {
            Counter counter = new Counter();
            Thread t1 = new Thread(() -> {
                for (int i = 0; i < 500; i++) {
                    counter.increment();
                }
            }, "t1");
            Thread t2 = new Thread(() -> {
                for (int i = 0; i < 500; i++) {
                    counter.decrement();
                }
            }, "t2");
    
            t1.start();
            t2.start();
            t1.join();
            t2.join();
    
            log.debug("最终计数:{}", counter.getCount());
        }
    }
    

    当方法上不加synchronized时,结果不可预测。加上synchronized后,最终结果:0

3、锁对象

那么synchronized锁对象到底是对那个对象加锁了呢?

  • 静态方法:类对象
  • 成员方法:this对象
  • 普通代码块:参数里面的对象

下面以几个示例分析锁的是哪个对象,给出输出结果及分析锁对象,

  1. 输出结果:

    1. 1 2
    2. 2 1
    3. 锁对象:this
    @Slf4j(topic = "security.syn.LockObject")
    public class LockObject {
        public static void main(String[] args) {
            Number1 n = new Number1();
            new Thread(() -> {
                log.debug("begin");
                n.a();
            }).start();
            new Thread(() -> {
                log.debug("begin");
                n.b();
            }).start();
        }
    }
    
    @Slf4j(topic = "security.syn.Number1")
    public class Number1 {
        public synchronized void a() {
            log.debug("1");
        }
        public synchronized void b() {
            log.debug("2");
        }
    }
    
    
  2. 输出结果

    1. 等1s 1 2,图示:在这里插入图片描述
  3. 2 等1s 1,图示:在这里插入图片描述

  4. 锁对象:this

@Slf4j(topic = "security.syn.Number2")
public class Number2 {
    public synchronized void a() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}


  1. 输出结果

    1. 方法c没加synchronized,方法a加了sleep;可以确定的是3一定先与1输出
    2. 可能的情况:3->1s->1->2;3 ->2->1s-> 1;2->3->1s->1
    3. 锁对象:this
    @Slf4j(topic = "security.syn.LockObject1")
    public class LockObject {
        public static void main(String[] args) {
            Number3 n = new Number3();
            new Thread(() -> {
                log.debug("begin");
                n.a();
            }).start();
    
            new Thread(() -> {
                log.debug("begin");
                n.b();
            }).start();
    
            new Thread(() -> {
                log.debug("begin");
                n.c();
            }).start();
        }
    }
    
    @Slf4j(topic = "security.syn.Number3")
    public class Number3 {
        public synchronized void a() {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("1");
        }
        public synchronized void b() {
            log.debug("2");
        }
    
        public  void c() {
            log.debug("3");
        }
    }
    
    
  2. 输出结果:

    1. 因为是2个不同的对象,不存在互斥
    2. 2->1s->1
    3. 锁对象:this
    @Slf4j(topic = "security.syn.LockObject")
    public class LockObject {
        public static void main(String[] args) {
            Number4 n1 = new Number4();
            Number4 n2 = new Number4();
            new Thread(() -> {
                log.debug("begin");
                n1.a();
            }).start();
    
            new Thread(() -> {
                log.debug("begin");
                n2.b();
            }).start();
        }
    }
    
    @Slf4j(topic = "security.syn.Number4")
    public class Number4 {
        public synchronized void a() {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("1");
        }
        public synchronized void b() {
            log.debug("2");
        }
    }
    
    
  3. 输出结果:

    1. 方法a静态方法,锁对象:类对象
    2. 方法b成员方法,锁对象:this
    3. 不存在互斥,输出 2->1s->1
    @Slf4j(topic = "security.syn.LockObject")
    public class LockObject {
        public static void main(String[] args) {
            Number5 n = new Number5();
            new Thread(() -> {
                log.debug("begin");
                n.a();
            }).start();
    
            new Thread(() -> {
                log.debug("begin");
                n.b();
            }).start();
        }
    }
    
    @Slf4j(topic = "security.syn.Number5")
    public class Number5 {
        public static synchronized void a() {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("1");
        }
        public synchronized void b() {
            log.debug("2");
        }
    }
    
    
  4. 分析:

    1. 方法a,b都是静态方法,锁对象:同一类对象
    2. 不管new几个对象,都存在互斥关系,输出同1
    @Slf4j(topic = "security.syn.LockObject")
    public class LockObject {
        public static void main(String[] args) {
            Number6 n1 = new Number6();
            Number6 n2 = new Number6();
            new Thread(() -> {
                log.debug("begin");
                n1.a();
            }).start();
    
            new Thread(() -> {
                log.debug("begin");
                n2.b();
            }).start();
        }
    }
    
    @Slf4j(topic = "security.syn.Number5")
    public class Number6 {
        public static synchronized void a() {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("1");
        }
        public static synchronized void b() {
            log.debug("2");
        }
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值