您的位置  > 互联网

Java开发中线程同步的方法和修饰符的注意事项

为什么要同步线程?

在多线程开发中,多个线程可能会抢占一个资源,这可能会引起冲突。 也就是说,一个线程可能没有时间在另一个线程开始进行更改之前保存更改的资源。

这可能会导致数据不一致,因此需要引入多线程同步。 也就是说,多个线程中只有一个线程可以对共享资源进行更改,其他线程无法修改数据。

实现线程同步的方法

1. 关键词

修改整个方法

public synchronized void set(){}

注意:您还可以修改静态方法。 如果这个时候调用这个方法,整个类就会被锁住。

修改具体代码块

public void save(){  synchronized (this){    count += money  }}

PS:同步是一个高开销的操作,所以应该尽量减少同步的内容。 一般不需要同步整个方法,只需要同步相应的代码块即可。

我们举一个两个线程向银行账户存钱的小例子,其中使用修改方法来实现线程同步。

代码如下所示:

同步是一个高开销的操作,所以应该尽量减少同步内容,因此也可以使用修饰的代码块,而不是修饰整个方法。

2. 关键词

可见性是指一旦线程修改了被修改的变量,就会保证修改的值立即更新到主存中。 当其他线程需要读取时,可以立即获取修改后的值。

Java中为了加快程序的运行效率,对一些变量的操作通常先在线程的寄存器或者CPU缓存上进行,然后再同步到主存,而带有修饰符的变量则直接读写。 主存储器。

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

还是用刚才的例子,使用关键字可以这样实现:

3.锁

与Lock相比,Lock有很多优点。

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

既然这么方便,就可以轻松实现资源的同步互斥访问。 那么,为什么需要锁呢? 看看以下3种情况:

情况1:

使用该关键字时,如果持有锁的线程由于等待IO或者其他原因(比如调用sleep()方法)而被阻塞,但锁没有释放,那么其他线程就只能等待。

案例2:

多个线程读写文件时,读写操作会冲突,写操作也会冲突,但读操作不会冲突。 因此,使用关键字会带来一个问题:多个线程无法同时进行读操作。 这会极大地影响程序的执行效率。

案例3:

我们可以通过Lock知道线程是否成功获取了锁,但是却做不到。

锁可以解决以上三种情况描述的所有问题。 现在你知道为什么会有Lock这个东西了吧,哈哈!

使用类来定义锁,其中lock()方法打开锁,()方法关闭锁,与修改后的方法作用相同。 使用锁时,需要注意及时释放锁,否则会进入死锁状态。 一般情况下,锁会被释放。

还是之前的例子,代码如下:

代码输出如下:

可以看到,时间每增加10,就只有一个线程在进行,这就是线程同步。

4.

使用类来管理,但这种方式与同步机制不同。 该类管理的变量在每个线程中都有自己的副本。 各个副本之间是相互独立的,因此得到的结果与其他的不同。

还是用刚才的例子,代码实现如下:

结果如下图:

可以看到,修改后的程序中,就像两个账户相互独立、互不干扰一样,同样的变量访问冲突问题也得到了解决。

- -结束 - -