Java 多线程:synchronized 关键字用法(修饰类,方法,静态方法,代码块)

转载 2017年03月31日 22:37:44

前言

在 多线程生成的原因(Java内存模型与i++操作解析) 中,介绍了Java的内存模型,从而可能导致的多线程问题。synchronized就是避免这个问题的解决方法之一。除了 synchronized 的方式,还有 lock,condition,volatile,threadlocal,atomicInteger,cas等方式。

synchronized 用法

它的修饰对象有几种:

  1. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分, 作用的对象是这个类的所有对象
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法, 作用的对象是调用这个方法的对象
  3. 修改一个静态的方法,其作用的范围是整个静态方法, 作用的对象是这个类的所有对象
  4. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码, 作用的对象是调用这个代码块的对象

修饰一个类

其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象,如下代码:

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

修饰一个方法

synchronized 修饰一个方法很简单,就是在方法的前面加synchronized,例如:

public synchronized void method()
{
   // todo
}

另外,有几点需要注意:

  1. 在定义接口方法时不能使用synchronized关键字。
  2. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
  3. synchronized 关键字不能被继承 。如果子类覆盖了父类的 被 synchronized 关键字修饰的方法,那么子类的该方法只要没有 synchronized 关键字,那么就默认没有同步,也就是说,不能继承父类的 synchronized。

修饰静态方法

我们知道 静态方法是属于类的而不属于对象的 。同样的, synchronized修饰的静态方法锁定的是这个类的所有对象 。如下:

public synchronized static void method() {
   // todo
}

修饰代码块

  • 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
  • 当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
  • 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
  • 第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
  • 以上规则对其它对象锁同样适用.

JDK中对 synchronized 的使用举例

对于 synchronized ,个人觉得是一种比较粗糙的加锁,尤其是对整个对象,或者整个类进行加锁的时候。例如,HashTable,它相当于 HashMap的线程安全版本。实现如下:

public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

可以看到,很多方法都是使用了 synchronized 进行了修饰,那么就意味如果还有别的同步方法x,这个x方法和get方法即使在没有冲突的情况下也需要等待执行。这样效率上 必然会有一定的影响,所以会有 ConcurrentHashMap 进行分段加锁。

另外,在JDK中,Collections有一个方法可以把不是线程安全的集合转化为线性安全的集合,它是这样实现的:

public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
        return new SynchronizedCollection<>(c);
    }
 static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }

        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        }

        public int size() {
            synchronized (mutex) {return c.size();}
        }

        ......
}

可以看到 在构造函数中 有 mutex = this, 然后在具体的方法使用了 synchronized(mutex),这样就会对调用该方法的对象进行加锁。还是会出现HashTable 出现的那种问题,也就是效率不高。

以上就是 JDK 对于 synchronized 用法的简单举例。

参考


from: https://github.com/pzxwhc/MineKnowContainer/issues/7

Java中Synchronized的用法

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代...
  • luoweifu
  • luoweifu
  • 2015-06-24 00:25:01
  • 287132

java多线程(四)synchronized关键字修饰方法

在之前的博客中我们介绍了条件对象和锁对象,两者结合使用才能起到比较好的互斥与同步效果,大家可能觉得有些麻烦,有没有将两者结合起来的工具呢,有!java提供了synchronized关键字来实现线程的互...
  • xingjiarong
  • xingjiarong
  • 2015-08-24 08:49:30
  • 5028

synchronized 关键字可修饰 object和方法

 原文转自: http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html Java语言的关键字,当它用来修饰一个方法或者一...
  • wangliwei4321
  • wangliwei4321
  • 2017-02-15 13:56:53
  • 544

JAVA线程安全之synchronized关键字的正确用法

JAVA线程安全关于synchronized关键字的用法,今天才知道原来我一直错了。以为用了synchronized关键字包住了代码就可以线程同步安全了。 测试了下。发现是完全的错了。synchro...
  • yaerfeng
  • yaerfeng
  • 2012-02-13 14:14:25
  • 27579

synchronized 修饰方法

这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(...
  • opmic
  • opmic
  • 2012-08-15 23:51:03
  • 720

synchronized关键字锁住的是对象还是代码块

synchronized 修饰的是对象,而不是代码块。测试代码来自于博客http://www.cnblogs.com/QQParadise/articles/5059824.html 下面两端代码都...
  • upwind_fly
  • upwind_fly
  • 2017-06-13 16:19:07
  • 207

Java基础之《synchronized关键字用在方法和代码块上的区别》

在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。问题的由来:看...
  • csj50
  • csj50
  • 2018-03-09 14:01:12
  • 47

使用Synchronized关键字同步类方法

synchronized关键字有两种用法,一种是只用于方法的定义中,另外一种是synchronized块,我们不仅可以使用synchronized来同步一个对象变量,你也可以通synchronized...
  • lonely_fireworks
  • lonely_fireworks
  • 2012-02-26 17:44:37
  • 5889

java中synchronized修饰代码块(两种创建线程的方式讲解卖票程序)

格式: synchronized(类对象名 aa) { //同步代码块 } 功能: synchronized(类对象名 aa)的含义是:判断aa是否已经被其他线程所霸占,如果发现已经被...
  • Rain722
  • Rain722
  • 2016-09-06 15:03:57
  • 1143

java中synchronized修饰的方法或代码块和static synchronized修饰方法或代码块的区别

例如:现在有两个方法 public class MyService { synchronized public void methodA() throws InterruptedExce...
  • hou314159
  • hou314159
  • 2017-08-08 10:02:39
  • 467
收藏助手
不良信息举报
您举报文章:Java 多线程:synchronized 关键字用法(修饰类,方法,静态方法,代码块)
举报原因:
原因补充:

(最多只允许输入30个字)