精华内容
下载资源
问答
  • java多线程高并发线程安全问题

    千次阅读 2017-06-26 11:40:29
    在抢购之类的业务处理时,会出现超发之类的线程安全问题。我个人想到的解决方法如下:采用数据库锁,悲观锁有效率低下问题,所以我推荐乐观锁,虽然会增大CPU开销,很多服务和软件都支持乐观锁,如Redis的watch采用...

    在抢购之类的业务处理时,会出现超发之类的线程安全问题。

    我个人想到的解决方法如下:

    1. 采用数据库锁,悲观锁有效率低下问题,所以我推荐乐观锁,虽然会增大CPU开销,很多服务和软件都支持乐观锁,如Redis的watch
    2. 采用FIFO队列,强行把多线程变成单线程,但是也会出现队列内存爆满问题
    3. 采用同步代码块,只给数据库操作的代码加锁,提高效率,对不同数据记录操作采用不同的锁
    展开全文
  • 高并发下才会出现异常 private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat...
    高并发下才会出现异常
    
    
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    
    public static void main(String[] ages) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = Calendar.getInstance();
    
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 500; i++) {
            executorService.execute(() -> {
                try {
                    System.out.println(sdf.format(calendar.getTime()));
                    System.out.println(df.get().parse("2019-06-15 16:35:20"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
    展开全文
  • 为了解决线程安全问题,我们有3个思路: 第一每个线程独享自己的操作对象,也就是多例,多例势必会带来堆内存占用、频繁GC、对象初始化性能开销等待等一些列问题。 第二单例模式枷锁,典型的案例是HashTable和...

    在多例的情况下,每个对象在堆中声明内存空间,多线程对应的Java栈中的句柄或指针指向堆中不同的对象,对象各自变量的变更只会印象到对应的栈,也就是对应的线程中,不会影响到其它线程。所以多例的情况下不需要考虑线程安全的问题,因为一定是安全的。


    而在单例的情况下却完全不一样了,在堆中只有一个对象,多线程对应的Java栈中的句柄或指针指向同一个对象,方法的参数变量和方法内变量是线程安全的,因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。但是成员变量只有一份,所有指向堆中该对象的句柄或指针都可以随时修改和读取它,所以是非线程安全的。

     


    为了解决线程安全的问题,我们有3个思路:

    第一每个线程独享自己的操作对象,也就是多例,多例势必会带来堆内存占用、频繁GC、对象初始化性能开销等待等一些列问题。

    第二单例模式枷锁,典型的案例是HashTableHashMap,对读取和变更的操作用synchronized限制起来,保证同一时间只有一个线程可以操作该对象。虽然解决了内存、回收、构造、初始化等问题,但是势必会因为锁竞争带来高并发下性能的下降。

    第三个思路就是今天重点推出的ThreadLocal。单例模式下通过某种机制维护成员变量不同线程的版本。


     

    假设三个人想从镜子中看自己,第一个方案就是每人发一个镜子互不干扰,第二个方案就是只有一个镜子,一个人站在镜子前其他人要排队等候,第三个方案就是我这里发明了一种“魔镜”,所有人站在镜子前可以并且只能看到自己!!!

     

    主程序:

    public static void main(String[] args) {
    		//Mirror是个单例的,只构建了一个对象
    		Mirror mirror = new Mirror("魔镜");
    		
    		//三个线程都在用这面镜子
    		MirrorThread thread1 = new MirrorThread(mirror,"张三");
    		MirrorThread thread2 = new MirrorThread(mirror,"李四");
    		MirrorThread thread3 = new MirrorThread(mirror,"王二");
    		
    		thread1.start();
    		thread2.start();
    		thread3.start();
    	}

    很好理解,创建了一面镜子,3个人一起照镜子。

    MirrorThread

    public class MirrorThread extends Thread{
    	private Mirror mirror;
    	
    	private String threadName;
    	
    	public MirrorThread(Mirror mirror, String threadName){
    		this.mirror = mirror;
    		this.threadName = threadName;
    	}
    		
    	//照镜子
    	public String lookMirror() {
    		return threadName+" looks like "+ mirror.getNowLookLike().get();
    	}
    	
    	//化妆
    	public void makeup(String makeupString) {
    		mirror.getNowLookLike().set(makeupString);
    	}
    	
    	@Override
        public void run() {
    		int i = 1;//阈值
    		while(i<5) {
    			try {
    				long nowFace = (long)(Math.random()*5000);
    				sleep(nowFace);
    				StringBuffer sb = new StringBuffer();
    				sb.append("第"+i+"轮从");
    				sb.append(lookMirror());
    				makeup(String.valueOf(nowFace));
    				sb.append("变为");
    				sb.append(lookMirror());
    				System.out.println(sb);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			i++;
    		}
    	}
    }

    也很好理解,就是不断的更新自己的外貌同时从镜子里读取自己的外貌。


    重点是Mirror:

    public class Mirror {
    	private String mirrorName;
    	
    	//每个人要看到自己的样子,所以这里要用ThreadLocal
    	private ThreadLocal<String> nowLookLike;
    	
    	public Mirror(String mirrorName){
    		this.mirrorName=mirrorName;
    		nowLookLike = new ThreadLocal<String>();
    	}
    
    	public String getMirrorName() {
    		return mirrorName;
    	}
    
    	public ThreadLocal<String> getNowLookLike() {
    		return nowLookLike;
    	}
    }

    对每个人长的样子用ThreadLocal类型来表示。

    先看测试结果:

    第1轮从张三 looks like null变为张三 looks like 3008
    第2轮从张三 looks like 3008变为张三 looks like 490
    第1轮从王二 looks like null变为王二 looks like 3982
    第1轮从李四 looks like null变为李四 looks like 4390
    第2轮从王二 looks like 3982变为王二 looks like 1415
    第2轮从李四 looks like 4390变为李四 looks like 1255
    第3轮从王二 looks like 1415变为王二 looks like 758
    第3轮从张三 looks like 490变为张三 looks like 2746
    第3轮从李四 looks like 1255变为李四 looks like 845
    第4轮从李四 looks like 845变为李四 looks like 1123
    第4轮从张三 looks like 2746变为张三 looks like 2126
    第4轮从王二 looks like 758变为王二 looks like 4516
    

    OK,一面镜子所有人一起照,而且每个人都只能看的到自己的变化,这就达成了单例线程安全的目的。

     

    我们来细看下它是怎么实现的。

    先来看Thread

    Thread中维护了一个ThreadLocal.ThreadLocalMapthreadLocals = null; ThreadLocalMap这个MapkeyThreadLocalvalue是维护的成员变量。现在的跟踪链是Thread->ThreadLocalMap-><ThreadLocal,Object>,那么我们只要搞明白Thread怎么跟ThreadLocal关联的,从线程里找到自己关心的成员变量的快照这条线就通了。

    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                table = new Entry[INITIAL_CAPACITY];
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                table[i] = new Entry(firstKey, firstValue);
                size = 1;
                setThreshold(INITIAL_CAPACITY);
            }

    再来看ThreadLocal:它里面核心方法两个get()set(T)

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }

    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }


    方法里通过Thread.currentThread()的方法得到当前线程,然后做为key存储到当前线程对象的threadLocals中,也就是TreadLocalMap中。

    OK,这样整个关系链已经建立,真正要去访问的成员变量在一个map中,key是线程号,值是属于该线程的快照。

    ThreadLocal里还有map的创建createMap(t, value)、取值时对象的初始值setInitialValue()、线程结束时对象的释放remove()等细节,有兴趣的可以继续跟进了解下。

     

    ThreadLocal应用其实很多,例如Spring容器中实例默认是单例的,transactionManager也一样,那么事务在处理时单例的manager是如何控制每个线程的事务要如何处理呢,这里面就应用了大量的ThreadLocal






    展开全文
  • spring单例在高并发下可能出现的错误: 首先,只有当注入的对象是无状态的幂等的才可以保证执行前后不被修改,否则执行一次之后单例对象就会发生改变,在下次执行有肯能造成结果不一样,当在高并发的情况下就会出现...

    在单例模式下,如果类中存在全局变量或者父类中存在全局变量,多个线程并发请求的时候,会出现全局变量的值的混乱和冲突。

    引用资料

    spring单例在高并发下可能出现的错误: 首先,只有当注入的对象是无状态的幂等的才可以保证执行前后不被修改,否则执行一次之后单例对象就会发生改变,在下次执行有肯能造成结果不一样,当在高并发的情况下就会出现,这个线程刚使用单例对象进行属性设置,还未使用的情况下,另一个进程已经将单利对象的数据进行修改属性完成,则远来线程获取到的单例就是一个脏对象不可使用。 当单例对象中含有变化的变量数据,则就不可以使用对象注入的形式注入,那怎么办?
    1、在单例对象中进行new对象,这属于线程自己的内存进行存放数据,其他线程无法使用
    2、在单例对象中注入了可变对象,则在使用的时候进行copy浅拷贝后使用拷贝后的对象不实用单例对象。
    3、通过new Callable()轻量级的创建局部变量可以使用上下文变量,属于线程自己的变量。
    总结:在高并发情况下,单利对象的数据不可以在一个线程使用过,另一个线程调用时单例对象的数据发生改变。 其实单例对象相当于全局变量,线程执行时需要修改数据,再高并发的情况下就会出现当前线程获取到的单例对象数据是脏数据。

    Spring单例与线程安全小结

    一、Spring单例模式与线程安全

    Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。

    单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
    当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对该单列状态的修改(体现为该单列的成员属性),则必须考虑线程同步问题
    同步机制的比较  ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

    在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

    而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

    由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用
     概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

    Spring使用ThreadLocal解决线程安全问题

    我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

    一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程
    ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。  线程安全问题都是由全局变量及静态变量引起的。
    若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
    1) 常量始终是线程安全的,因为只存在读操作。
    2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
    3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
    有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
    无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
    有状态对象:
    无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
    Struts2默认的实现是Prototype模式。也就是每个请求都新生成一个Action实例,所以不存在线程安全问题。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。

    有状态的bean和无状态的bean

    有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
    无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

    我的理解:一般类中存在全局变量或者静态变量就是有状态的bean.例如实体类,mvc中的model。不存在全局变量的bean,一般为无状态的bean,例如mvc中的dao,service层或者是工具类。有状态的bean,是线程不安全的,无状态的bean是线程安全的。
    线程安全:就是在高并发等的情况下,两个线程同时调用一个有状态的bean,当第一个线程未使用bean的变量时,被第二个线程修改了变量的值,造成bean中的全局变量或者静态变量的值被覆盖而混乱。
    springMVC的bean默认情况下是单例模式,我曾有一次,使几个同类型的service继承了一个有状态的baseService,结果造成了登陆账户的混乱。
    单例模式下建议不要使用有状态的bean,如果不可避免可以用TreadLoad解决,它是线程安全的。另外在方法内,建议使用基本类型。

    Out,Request,Response,Session,Config,Page,PageContext是线程安全的,Application在整个系统内被使用,所以不是线程安全的.
    
    展开全文
  • 高并发线程安全

    2020-06-21 19:15:18
    高并发线程安全的介绍 高并发: 是指在某个时间点上,有大量的用户(线程)同时访问同一资源 线程安全:在某个时间点上,当大量用户(线程)访问同一资源时,由于多线程运行机制的原因,可能会导致被访问的资源出现...
  • 高并发线程安全 1.高并发线程安全 高并发:就是在一段时间内有大量的线程要执行. 双11,春运12306,大学选选修课 ​线程安全:在高并发的情况下,多个线程之间有互相影响的效果。 2.多线程内存运行机制 当一个...
  • 本文介绍了 Java 中高并发线程安全的相关问题。。。
  • 2.线程安全问题 非线程安全主要是指多个线程对同一个对象的实例进行操作时,会出现被更改,值不同的情况。 线程安全问题表现为三个方面: 原子性、可见性、有序性 原子性: 不可分割的意思。原子操作的不可分割有两...
  • 在读多写少的场景下,我们一般会用读写锁 ReadWriteLock来保证共享对象的线程安全性。 public Object read() { lock.readLock().lock(); // 对ArrayList读取 lock.readLock().unlock(); } public void write()...
  • 内容相对简单,但多线程的知识肯定不会这么简单,否则我们也不需要花这么多心思去学习,因为多线程中容易出现线程安全问题。那么什么是线程安全呢,定义如下:当多个线程访问同一个对象时,如果不用考虑这些线程在...
  • 实际工作中,经常会遇到多线程并发时的类似抢购的功能,本篇描述一个简单的redis分布式锁实现的多线程抢票功能。直接上代码。首先按照慣例,给出一个错误的示范:我们可以看看,当20个线程一起来抢10张票的时候,会...
  • 减库存可以采用同步调用(商品微服务提供接口,通过Feign调用),也可以采用异步...若库存不足,则减库存失败,但是订单微服务中并不知道减库存失败,因此事务不会回滚,这就是分布式事务问题 (跨服务的事务)。我...
  • 高并发之——SimpleDateFormat类的线程安全问题和解决方案 原文链接:https://blog.csdn.net/l1028386804/article/details/104397090 首先问下大家:你使用的SimpleDateFormat类还安全吗?为什么说...
  • Java高并发--线程安全策略不可变对象发布不可变对象可保证线程安全。实现不可变对象有哪些要注意的地方?比如JDK中的String类。不提供setter方法(包括修改字段、字段引用到的的对象等方法)将所有字段设置为final、...
  • 高并发场景下ArrayList线程安全问题解析 1.在高并发下ArrayList存在并发修改异常 package com.example.demo; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util....
  • 实际工作中,经常会遇到多线程并发时的类似抢购的功能,本篇描述一个简单的redis分布式锁实现的多线程抢票功能。 直接上代码。首先按照慣例,給出一個错误的示范: 我们可以看看,当20个线程一起来抢10张票的时候...
  • 多线程入门 高并发安全性问题.volatile关键字和原子类讲解第一章 多线程1.1并发与并行1.2线程与进程1.3...高并发线程安全2.1高并发线程安全2.2多线程的安全性问题-可见性2.3多线程的安全性问题-有序性2.4多...
  • 所以在使用多线程并发地访问这些容器时可能出现线程安全问题。因此要求开发人员在任何用到这些的地方需要做同步处理。如此导致使用时极为不便。对此,java中提供了一些相应的同步容器供使用。 2、常见的同步容器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,651
精华内容 660
关键字:

高并发线程安全问题