线程得原子性、可见性

原子性

  定义:原子的字面意思是不可分割的。对于涉及访问共享变量的操作,若该操作从其执行线程以外的任意线程来看是不可分割的,那么该操作就是原子操作,相应的我们称该操作具有原子性。

所谓不可分割,其中一个含义是指访问(读、写)共享变量的操作从其执行线程以外的任何线程来看,该操作要么已经执行结束要么还没开始执行,即其他线程不会看到该操作执行的中间效果。

其二是如果两个原子操作同时访问某个共享变量,那么其中一个线程执行期间,另外得线程无法执行,也就是说访问同一组共享变量得原子操作是不能被交的。

  下面通过一个例子来体会一下不可分割的含义:


/**
* @ClassName AtomicTest
* @Description TODO
* @Author liuyi
* @Date 2020/8/5 22:42
* @Version 1.0
*/
public class AtomicTest {

public static void main(String[] args) {
for (int i = 0; i <100 ; i++) {
HostInfo hostInfo = new HostInfo("127.0.0.0",8080);
//更新端口
new Thread(()->{
hostInfo.setIp("127.0.0.1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
hostInfo.setPort(8082);
}).start();
//连接端口
new Thread(()->hostInfo.connect()).start();

}
}
}
class HostInfo{
private String ip;
private int port;

public HostInfo(String ip, int port) {
this.ip = ip;
this.port = port;
}

public String getIp() {
return ip;
}

public void setIp(String ip) {
this.ip = ip;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

private void update(String ip, int port){
this.ip = ip;
this.port = port;
}

public void connect(){
System.out.println(this.ip+":"+this.port);
}
}
 

 我们来模拟通过ip和端口去连接某个服务,连接之前我们先获取服务的ip和端口,假设我们开启两个线程,一个是更新端口的线程,一个是连接服务端口的线程,

可以看到,我们去连接服务的时候,可能会出现只更新了ip,端口还没更新,很明显这不是我们想要的结果,所以,更新ip和端口不是一个原子操作。

那怎么保证操作的原子性呢,java有两种方式来实现原子性,一是通过加锁(lock)的方式,锁具有排他性,它能保证一个共享变量在任意时刻只能被一个线程访问。

另外一种是利用处理器提供的CAS指令,CAS指令实现原子性的方式与锁实现原子性的方式实质上是相同的,区别在于锁是在软件这一层次实现,而CAS是直接在硬件

(内存和处理器)这一层实现,也可以被看作硬件锁。

可见性:

在多线程环境下,一个线程对某个共享变量进行更新后,后续访问该变量的线程可能无法立刻读取到这个更新的结果,甚至永远也无法读取到这个更新结果。这就是线程

安全问题的另外一个表现形式:可见性。

举个简单的例子,模拟比赛的场景,启动一个线程进行比赛,当裁判判定某个对赢得比赛就将flag设置为-1,然后线程根据判断flag得值是否等于-1去中止比赛。

/**
 * @Author liuyi
 * @Description //TODO
 * @Date 22:30 2020/8/8
 * @Param  
 * @return 
 **/
public class Test {


    public static void main(String[] args) {
        Match match = new Match();
        match.start();
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        match.flag= -1;
        System.out.println("change.....");
    }
}


class Match extends Thread {
    public int flag = 0;

    @Override
    public void run() {
        System.out.println("Match Start");
        while (true) {
            if (flag == -1) {
                break;
            }
        }
        System.out.println("Match End");
    }
}
View Code

但是我们来看代码得运行结果:

 根本没有打印比赛结束,并且程序一直在运行中,这说明flag对于Match线程来讲,根本没有更新为-1。所以此时,共享变量flag对于Match线程不具备可见性。

那么如何实现可见性呢,对共享变量加volalite关键字即可实现可见性。

原文地址:https://www.cnblogs.com/liu-yi/p/13443496.html