首页 > 技术文章 > 实现线程同步的几种方式

volvane 2018-07-31 10:32 原文

synchronized关键字修饰的方法实现同步

1.在方法级别  public synchronized ….

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

 

2.同步代码块   synchronized(对象){}

即有synchronized关键字修饰的语句块。 

通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

/**
             * 用同步代码块实现
             * 
             * @param money
             */
            public void save1(int money) {
                synchronized (this) {
                    account += money;
                }
            }

 

 

1.当synchronized作用在方法上的时候,锁住的就是这个对象的实例 synchronized(this). 

2.当一个线程访问synchronized(this) 同步块时, 另一个线程仍然可以访问当前对象内的非synchroinzed(this)同步块代码

3.同步是一个耗性能的操作,因此我们尽量减少同步的内容,最好不要加载整个方法上

 

3.给对象加锁

当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序。

public void method3(SomeObject obj)
{
   //obj 锁定的对象
   synchronized(obj)
   {
      // todo
   }
}

 

用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。

 

 4.修饰一个静态的方法

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

静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

 

当synchronized修饰静态方法的时候, 同步对象就是这个类的类对象

如代码中的例子,当第一个线程进入method1的时候,需要占用TestReflection.class才能执行。

第二个线程进入method2的时候进去不了,只有等第一个线程释放了对TestReflection.class的占用,才能够执行。 反推过来,第二个线程也是需要占用TestReflection.class。 那么TestReflection.class就是method2的同步对象。

 

5. 修饰一个类

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

给class加锁和上例的给静态方法加锁是一样的,所有对象公用一把锁

第二种:(volatile)实现线程同步 

volatile关键字为域变量的访问提供了一种免锁机制

只需在account前面加上volatile修饰,即可实现线程同步。

class Bank {
            //需要同步的变量加上volatile
            private volatile int account = 100;

            public int getAccount() {
                return account;
            }
            //这里不再需要synchronized 
            public void save(int money) {
                account += money;
            }
        }

1.多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。

2.volatile不能保证原子操作,因此volatile不能代替synchronized

3.每次要线程要访问volatile修饰的变量时都是从内存中读取,而不是从缓存当中读取,因此每个线程访问到的变量值都是一样的。这样就保证了同步。

java.util.concurrent包来支持同步

推荐阅读