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修饰的变量时都是从内存中读取,而不是从缓存当中读取,因此每个线程访问到的变量值都是一样的。这样就保证了同步。