并发编程之Synchronized

313 阅读3分钟

在介绍Synchrinized之前,我们先来了解一下并发的相关概念。

什么是并发?

并发: 同一时间段,多个任务都在执行 (单位时间内不一定同时执行);在同一时刻只会有一条指令执行,但是多个指令被快速轮换执行,这个切换的时间非常短以至于我们不用去考虑它,这就是并发的基本含义。

而多线程正是我们可以完成并发的很重要的执行手段。现在的系统动不动就要求百万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程的特性可以大大的提高我们的系统运行的效率。而且当今正值多核CPU时代,多线程也可以提高我们CPU的利用效率,最大化的利用我们现有的资源。

上文说了多线程这么多的好处,那么它有什么缺点呢?

我们在多线程的环境下,系统的执行顺序并不是由我们控制的,执行的线程就可能随时切换到其他线程中执行。这就可能会造成一些安全性的问题。 比如小A小B两个人同时在抢票(仅剩一张),小A买票的线程先执行,判断 num 是否大于0(现在 num =1),然后 num--。执行完这个操作之后,小B线程也开始执行,也开始判断 num 是否大于0,因为小A更新的num尚未更新到主存中,小B的工作空间的 num 值还是大于1,所以也执行 num--。

这个时候问题就出现了,只有一张票,做了两次 num--,最后num的值为-1系统就出现了错误。那么如何防止这种现象出现呢?这里我们就引入了 Synchronzied。

什么是Synchronzied?

Synchronized 关键字解决的是多个线程之间访问资源的同步性,Synchronized 关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

还用刚才的买票问题举例,下面是代码


   while (true) {
         synchronized (obj) {
                if (ticket > 0) {
                       try {
                             Thread.sleep(100);
                           } catch (InterruptedException e) {
                                 // TODO Auto-generated catch block
                            e.printStackTrace();
                          }
                       System.out.println(Thread.currentThread().getName() +  ":" + ticket--);
                }
         }
   }


这次我们使用了 Synchronized 来对整个 if 语句进行修饰,就保证了if语句的原子性,只有当整个 if 语句的代码执行完毕的时候,才会释放锁,我们也就很容易的解决了买票的问题。

下面我们来介绍一下 Synchronzied 的主要使用方式

synchronized 关键字最主要的三种使用方式:

* 修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

* 修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

* 修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

这些就是今天介绍 Synchronized 的全部内容,今后我也会持续在我的专栏中更新更多有价值的内容,欢迎关注。