• 线程得原子性、可见性


    原子性

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

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

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

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

    
    
    /**
    * @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关键字即可实现可见性。

  • 相关阅读:
    WEB之CSS3系列笔记
    WEB之HTML5 系列笔记
    WEB之CSS系列笔记
    WEB 之 HTML 系列笔记
    npm安装(install)出现EPROTO: protocol error, symlink 解决方法
    笔记本电脑联网时没有WIFI列表选项可选
    Windows系统中下载Earthdata数据 2
    matlab cell
    【Linux系统】开启TFTP服务
    【Linux】搭建交叉编译环境
  • 原文地址:https://www.cnblogs.com/liu-yi/p/13443496.html
Copyright © 2020-2023  润新知