多线程共享变量问题
来源:互联网 发布:2017淘宝现在还能刷吗 编辑:程序博客网 时间:2024/05/18 03:11
非线程安全代码举例
public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; }}
NoVisibility可能会持续循环下去,因为读线程可能永远看不到ready的值。
另一种更奇怪的现象是,NoVisibility可能输出0,因为读线程看到了写入ready的值,但却没有看到之后写入number的值,这种现象称为“重排序”。
使用同步可以保证可见性
@NotThreadSafepublic class MutableInteger { private int value; public int get() { return value; } public void set(int value) { this.value = value; }}
@ThreadSafepublic class SynchronizedInteger { @GuardedBy("this") private int value; public synchronized int get() { return value; } public synchronized void set(int value) { this.value = value; }}
对get()方法进行同步,保证了value的可见性。
非原子的64位操作
当线程没有同步的情况下读取变量,可能会得到一个失效的值,但是至少这个值是由之前某个线程设置的值,而不是一个随机值。这种安全性保证也被称为最低安全性。
最低安全性适合绝大多数变量,但是存在一个例外:非volatile类型的64位数值变量(double和long)。JVM允许64位的读操作或写操作分解为两个32位的操作。
因此,即使不考虑失效数据问题,在多线程程序中使用共享且可变的long和double等类型的变量也是不安全的,除非用关键字volatile来声明,或者用锁保护起来。
volatile关键字
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 禁止进行指令重排序。
发布与溢出
发布:使对象能够在除当前作用域之外的地方使用。
溢出:指某个对象不应该发布却被发布了。
public class PublishAndEscape { //发布status public static String status = "status"; private Object[] objects; // 内部的可变状态溢出,导致外部可以直接访问并修改object public Object[] getObjects() { return objects; } }
改进代码
public class PublishAndEscape { public static final String STATUS = "status"; private Object[] object; //参见ArrayList、CopyOnWriteList public Object[] getObject() { return Arrays.copyOf(object, object.length); } }
线程封闭
当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术叫做线程封闭(Thread Confinement)。
栈封闭
栈限制是线程限制的一种特例,在栈限制中,只能通过本地变量才可以触及对象。正如封装使不变约束更容易被保持,本地变量使对象更容易被限制在线程本地中。本地变量本身就被限制在执行线程中;它们存在于执行线程栈。其他线程无法访问这个栈。栈限制(也称线程内部或者线程本地用法,但是不要与核心库类的ThreadLocal混淆)
public int loadTheArk(Collection<Animal> candidates) { SortedSet<Animal> animals; int numPairs = 0; Animal candidate = null; //animals被封装在方法中,不要使它们溢出 animals = new TreeSet<Animal>(new SpeciesGenderComparator()); animals.addAll(candidates); for(Animal a:animals){ if(candidate==null || !candidate.isPotentialMate(a)){ candidate = a; }else{ ark.load(new AnimalPair(candidate,a)); ++numPairs; candidate = null; } } return numPairs; }
ThreadLocal
这个类能使线程中的某个值与保存值的对象关联起来。
ThreadLocal提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。
当某个线程终止后,与线程对应的对象会作为垃圾回收。
ThreadLocal对象通常用于防止对可变的单实例变量(Singleton)或全局变量进行共享。
public class ConnectionDispenser { static String DB_URL = "jdbc:mysql://localhost/mydatabase"; private ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { try { return DriverManager.getConnection(DB_URL); } catch (SQLException e) { throw new RuntimeException("Unable to acquire Connection, e"); } }; }; public Connection getConnection() { return connectionHolder.get(); }}
- 多线程共享变量问题
- 多线程中的使用共享变量的问题
- 多线程中的使用共享变量的问题
- 多线程中的使用共享变量的问题
- 多线程中的使用共享变量的问题
- 多线程的共享变量
- Java 多线程 变量共享
- 多线程共享变量方式
- java多线程共享变量
- Thread_MultiShareData(多线程共享变量)
- 多线程共享变量
- 多线程共享变量
- 多线程:共享变量
- 多线程共享变量
- 由一个多线程共享Integer类变量问题引起的。。。
- linux多线程中的共享变量
- 多线程共享实例变量例子
- 进程中多线程共享变量
- cisco交换机删除已经分配的dhcp地址绑定关系(binding for this client already exists)
- Linux安装jdk
- Java中的日期操作
- 无线通信频率分配表(详细)
- flannel
- 多线程共享变量问题
- HTML和CSS入门(3)
- 右值引用与转移语义
- Android TextView中显示单行过长的用...代替
- python 正则语法
- seekg(0,ios::beg)不起作用的原因和解决方法
- 微信公众号点击子菜单获取文章列表
- 消失多年的老人正在盼望后来人
- 关于css中的box-sizing:border-box