实现线程同步的3种方法

05-21 11:061245浏览

Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,将会导致数据不准确,相互之间产生冲突,因此,实现线程同步以避免在该线程没有完成操作之前,被其他线程调用,从而保证了该变量的唯一性和准确性。一般情况下,实现线程同步有以下3种方法:

1.synchronized关键字

1)Synchronized修饰整个方法

例:
public synchronized void set(){}

注意:synchronized也可以修饰静态方法,如果此时调用这个方法,将会锁住整个类。

2) synchronized修饰具体的代码块

public void save(){

synchronized (this){
  count += money
}
}

PS : 同步是一种高开销的操作,因此,应该尽量减少同步的内容。一般情况下不需要同步整个方法,只需同步对应的代码块即可。

2.Lock

小编认为Lock与Synchronized比较起来,Lock的优点还是比较多的。

如果一个代码块被synchronized关键字修饰,当一个线程执行该代码块时,其他的线程便只能一直等待直到占有锁的线程释放锁。那么,什么情况下占有锁的线程会释放锁呢 ?

既然synchronized如此方便,可以轻松实现对资源的同步互斥访问。那么,为什么还需要Lock呢 ?考虑以下3种情况:

Case 1 :

在使用synchronized关键字时,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep()方法)被阻塞了,但是又没有使释放锁,那么其他的线程就只能一直等待。

Case 2:

多个线程读写文件时,读和写操作会发生冲突,写和写操作也会发生冲突,但是,读和读操作不会发生冲突。所以使用synchronized关键字就会导致一个问题:无法实现多个线程同步进行读操作。这就会极大的影响程序的执行效率。

Case 3:

我们可以通过Lock得知线程有没有成功获取锁,但是synchronized无法做到。

以上三个Case中所描述的问题,Lock都可以解决,现在知道为什么会有Lock 这个东西了吧哈哈!

下面我们简单来说一下使用Lock实现线程同步的方法:

lock.lock() 加锁;lock.unlock() 释放锁。

public class Count{
Lock lock = new ReentrantLock();
public void save(){
for(int i=0;i<10;i++)
{ count++;
System.out.println("count:"+count);
}
lock.unlock();
}
}

3.volatile关键字

1)volatile是变量修饰符,其修饰的变量具有可见性。

可见性就是说一旦某个线程修改了被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取的时候,可以立即获取修改之后的值。

在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。

2)volatile可以禁止指令重排

指令重排是指编译器或者CPU为了提高程序的运行效率,可能会对输入的代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码中的执行结果是一致的,应用条件是单线程条件,对于并发多线程的场景下,指令重排会产生不确定的结果。

想要了解更多的线程相关的知识,我们可以观看动力节点在线的Java多线程视频课程,名师讲解,帮助我们攻克Java多线程的难关。

0人推荐
共同学习,写下你的评论

2 {{item.nickname}}

{{item.create_time}}

  {{item.zan}}