1.问题(为什么要实现原子性(同步)与及可见性)?
在高并发开发环境中,如果不控制数据的原子性与及可见性,可能会导致数据的不一致性,也可能导致系统阻塞,严重破坏了系统的稳定性。
原子性:即保证数据在同一时段只允许一个线程进行操作
可见性:即保证线程在工作内存中修改完成后,在释放锁之前,需要把数据写回到主内存当中。
2.代码实现与及验证
/**
* 资源类
*/
class MyDataSynchronized {
//共享变量
private int number = 1;
/**
* 无锁 写操作,不保证原子性
*/
public void notLockWrite() {
if (notLockRead() == 2) {
System.out.println(Thread.currentThread().getName() + "\t " + "无需修改!");
return;
}
System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
this.number = 2;
}
/**
* 无锁 读操作,不保证可见性
*/
public int notLockRead() {
return this.number;
}
}
/**
* synchronized能够实现原子性(同步)、可见性
*/
public class SynchronizedDemo {
public static void main(String[] args) {
notSync();
}
/**
* 无锁
*/
private static void notSync() {
MyDataSynchronized myDataSynchronized = new MyDataSynchronized();
//n个线程读
for (int i = 1; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t start!");
//监测number值是否改变,改变即及时退出
while (myDataSynchronized.notLockRead() == 1) {
}
System.out.println(Thread.currentThread().getName() + "\t end : " + myDataSynchronized.notLockRead());
}, "Read "+String.valueOf(i)).start();
}
//n个线程写
for (int i = 1; i < 6; i++) {
new Thread(() -> {
//保证读线程首先执行读取操作,也保证所有写线程能同时进行写操作
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
myDataSynchronized.notLockWrite();
}, "Write "+String.valueOf(i)).start();
}
}
}
验证:将共享变量的值进行修改,其中两个读取线程,五个写线程,为了读取线程先行运行,开启写线程时进行睡眠两秒,也方便模拟同时写操作。
预期结果:如果写线程只能执行成功一次写操作,读取线程在写操作完成后自行结束。
执行结果(线程多次修改,且读取线程进入阻塞,未能达到预期效果)
原因:读取线程进入阻塞,原因是修改线程修改的资源未写回主内存当中,修改的资源对于其它线程是不可见的。
线程多次修改原因主要是写线程同时读到number==1,各自执行写操作
修正:1.读取操作加synchronized,写操作不加。会导致多次修改
2.写操作加synchronized,读取操作不加。会导致读取线程阻塞
正确修改:在读与写操作同时加锁
/**
* 有锁 写操作,保证原子性
*/
public synchronized void useLockWrite() {
if (useLockRead() == 2) {
System.out.println(Thread.currentThread().getName() + "\t " + "无需修改!");
return;
}
System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
this.number = 2;
}
/**
* 有锁 读操作,保证可见性
*/
public synchronized int useLockRead() {
return this.number;
}
执行结果(读取线程正常完成,写线程只有一次写操作)
3.总结
synchronized关键字可实现多线程同步与及数据的可见性。
如上文章比较简陋,若其中有不恰当的地方,希望大家一起分享一下自己的意见。共同学习,一起成长。