精华内容
下载资源
问答
  • 首先说一下什么是线程安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程能进行访问直到该线程读取完,其他线程才可使用。不会出现数据一致或者数据污染。...

    1、为什么ArrayList线程不安全?

    首先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。如图,List接口下面有两个实现,一个是ArrayList,另外一个是vector从源码的角度来看,因为Vector的方法前加了,synchronized 关键字,也就是同步的意思,sun公司希望Vector是线程安全的,而希望arraylist是高效的,缺点就是另外的优点。说下原理(百度的,很好理解):一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成: 
    1.
    Items[Size] 的位置存放此元素; 
    2.
    增大 Size 的值。 
    在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1 
    而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。 
    那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是线程不安全了。 
    示例程序:

    package test;
    
     
    
    importjava.util.ArrayList;
    
    import java.util.List;
    
     
    
    public class ArrayListInThread implements Runnable{
    
        List<String> list1 = new ArrayList<String>();// not thread safe
    
     
    
    //    List<String> list1 =Collections.synchronizedList(new ArrayList<String>());// thread safe
    
        publicvoid run() {
    
            try {
    
                Thread.sleep((int)(Math.random() * 2));
    
            }
    
            catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }
    
            list1.add(Thread.currentThread().getName());
    
        }
    
     
    
        public static void main(String[] args) throws InterruptedException {
    
            ThreadGroup group = new ThreadGroup("mygroup");
    
            ArrayListInThread t = new ArrayListInThread();
    
            for (int i = 0; i < 10000; i++) {
    
                Thread th = new Thread(group, t,String.valueOf(i));
    
                th.start();
    
            }
    
     
    
            while (group.activeCount() > 0) {
    
                Thread.sleep(10);
    
            }
    
            System.out.println();
    
            System.out.println(t.list1.size()); // it should be 10000 if thread safecollection is used.
    
        }
    
    }

    2、不安全为什么要使用?

    这个ArrayList比线程安全的Vector效率高。

    3、如何解决线程不安全?

    一:使用synchronized关键字,这个大家应该都很熟悉了,不解释了;

    二:使用Collections.synchronizedList();使用方法如下:

            假如你创建的代码如下:List<Map<String,Object>>data=new ArrayList<Map<String,Object>>();

            那么为了解决这个线程安全问题你可以这么使用Collections.synchronizedList(),如:

           List<Map<String,Object>> data=Collections.synchronizedList(newArrayList<Map<String,Object>>());

           其他的都没变,使用的方法也几乎与ArrayList一样,大家可以参考下api文档;

    额外说下 ArrayListLinkedList;这两个都是接口List下的一个实现,用法都一样,但用的场所的有点不同,ArrayList适合于进行大量的随机访问的情况下使用,LinkedList适合在表中进行插入、删除时使用,二者都是非线程安全,解决方法同上(为了避免线程安全,以上采取的方法,特别是第二种,其实是非常损耗性能的)。



    展开全文
  • 线程同步:解决线程不安全问题

    千次阅读 2016-10-07 12:30:16
    当多个线程并发访问同一个资源对象时,可能会出现线程不安全的问题,比如现有50个苹果,现在有请三个童鞋(小A,小B,小C)上台表演吃苹果.因为A,B,C三个人可以同时吃苹果,此时使用多线程技术来实现这个案例. class ...

    当多个线程并发访问同一个资源对象时,可能会出现线程不安全的问题,比如现有50个苹果,现在有请三个童鞋(A,B,C)上台表演吃苹果.因为A,B,C三个人可以同时吃苹果,此时使用多线程技术来实现这个案例.

    class Apple implements Runnable{
    	private int num = 50;//苹果总数
    
    	public void run() {
    		for (int i = 0; i < 50; i++) {
    			if (num > 0) {
    				try {
    					//模拟网络延迟
    					Thread.sleep(100);
    					System.out.println(Thread.currentThread().getName() + "吃了编号为"
    							+ num-- + "的苹果");
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				
    			}
    		}
    	}
    	
    }
    public class AppleEatingImplements {
    	public static void main(String[] args) {
    		//创建三个线程(三个同学),吃苹果
    		Runnable apple = new Apple();
    		new Thread(apple,"A童鞋").start();
    		new Thread(apple,"B童鞋").start();
    		new Thread(apple,"C童鞋").start();
    	}
    
    }

    以上代码运行结果:


    为什么编号为39的苹果被吃了两次呢?

    AB线程拿到编号为39的苹果时,打印出来,有一个还没来得及做num--,而有一个做了num减一操作,num还剩38,这时候线程进入睡眠状态。这时候C线程来了,打印38,做减1操作,睡眠……

    要解决上述多线程并发访问多一个资源的安全性问题,就必须得保证打印苹果编号和苹果总数减1操作,必须同步完成.即是说,A线程进入操作的时候,BC线程只能在外等着,A操作结束,ABC才有机会进入代码去执行.

    解决多线程并发访问资源的安全问题,有三种方式:

    方式1:同步代码块

    方式2:同步方法

    方式3:锁机制(Lock)


    方式1:同步代码块

    语法:

    synchronized(同步锁)

    {

         需要同步操作的代码

    }

     

    同步锁:

    为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制.也称为同步监听对象/同步锁/同步监听器/互斥锁。

    实际上,对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁,谁拿到锁,谁就可以进入代码块,其他线程只能在代码块外面等着,而且注意,在任何时候,最多允许一个线程拥有同步锁.

    Java程序运行可以使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象.

    //同步代码块
    class Apple2 implements Runnable{
    	private int num = 100;
    	public void run() {
    		for(int i = 0; i < 50; i ++){
    			//this表示Apple2对象,该对象属于多线程共享的资源
    			synchronized(this){
    				if(num>0){
    					System.out.println(Thread.currentThread().getName()+"吃了"+num-- +"个苹果");
    				}
    			}
    		}
    		
    	}
    }
    
    public class SynchronizedBlockDemo {
    	public static void main(String[] args) {
    		Runnable a = new Apple2();
    		//三个线程表示三个人
    		new Thread(a,"小A").start();
    		new Thread(a,"小B").start();
    		new Thread(a,"小C").start();
    		
    	}
    
    }

    方式2:同步方法:

    使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着.

    Synchronized public void doWork(){

         ///TODO

    }

    同步锁是谁:

          对于非static方法,同步锁就是this.  

          对于static方法,我们使用当前方法所在类的字节码对象(Apple2.class).

     

    //同步方法
    class Apple3 implements Runnable{
    <span style="white-space:pre">	</span>private int num = 50;
    <span style="white-space:pre">	</span>public void run() {
    <span style="white-space:pre">		</span>for(int i = 0; i < 50; i ++){
    <span style="white-space:pre">			</span>try {
    <span style="white-space:pre">				</span>doWork();
    <span style="white-space:pre">			</span>} catch (InterruptedException e) {
    <span style="white-space:pre">				</span>// TODO Auto-generated catch block
    <span style="white-space:pre">				</span>e.printStackTrace();
    <span style="white-space:pre">			</span>}
    <span style="white-space:pre">		</span>}
    <span style="white-space:pre">	</span>}
    <span style="white-space:pre">	</span>synchronized private void doWork() throws InterruptedException {
    <span style="white-space:pre">			</span>if(num>0){
    <span style="white-space:pre">				</span>System.out.println(Thread.currentThread().getName()+"吃了"+num +"个苹果");
    <span style="white-space:pre">				</span>num --;
    <span style="white-space:pre">				</span>Thread.sleep(10);
    <span style="white-space:pre">			</span>}
    <span style="white-space:pre">		</span>
    <span style="white-space:pre">		</span>
    <span style="white-space:pre">	</span>}
    }
    
    
    public class SynchronizedMethodDemo {
    <span style="white-space:pre">	</span>public static void main(String[] args) {
    <span style="white-space:pre">		</span>Runnable a = new Apple3();
    <span style="white-space:pre">		</span>new Thread(a,"小A").start();//三个线程表示三个人
    <span style="white-space:pre">		</span>new Thread(a,"小B").start();
    <span style="white-space:pre">		</span>new Thread(a,"小C").start();
    <span style="white-space:pre">		</span>
    <span style="white-space:pre">	</span>}
    
    
    }
    

    注意:

    不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有的功能. 好比是多个线程出现串行.

     

    解决方案:把需要同步操作的代码定义在一个新的方法中,并且该方法使用synchronized修饰,再在run方法中调用该新的方法即可.

     

    实际上,同步代码块和同步方法差不了多少,在本质上是一样的,两者都用了一个关键字synchronizedsynchronized保证了多线程并发访问时的同步操作,避免线程的安全性问题,但是有一个弊端,就是使用synchronized的方法/代码块的性能比不用要低一些,因此如果要用synchronized,建议尽量减小synchronized的作用域。

    方式3:同步锁(锁机制)

    Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象.

    //锁机制
    class Apple4 implements Runnable{
    	private int num = 50;
    	//创建锁对象
    	private final Lock lock = new ReentrantLock();
    	public void run() {
    		for(int i = 0; i < 50; i ++){
    			doWork();
    		}
    	}
    	private void doWork() {
    		//进入方法,立马加锁
    		lock.lock();//获取锁
    		try {
    			//注意:if要放到try里,不然num为0时就不进入if中,最后锁就释放不了了
    			if(num>0){
    				System.out.println(Thread.currentThread().getName()+"吃了"+num +"个苹果");
    				num--;
    				Thread.sleep(10);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			//释放锁
    			lock.unlock();
    		}
    		
    	}
    }
    public class LockDemo {
    	public static void main(String[] args) {
    		Runnable a = new Apple4();
    		new Thread(a,"小A").start();//三个线程表示三个人
    		new Thread(a,"小B").start();
    		new Thread(a,"小C").start();
    	}
    }


    展开全文
  • java解决线程不安全问题的方法

    千次阅读 2017-07-10 21:56:18
    线程不安全问题 当多线程并发访问同一个资源时,容易出现不安全问题 有时候我们分析打印结果,发现没有问题,但是并是真的没问题,可能我们经验不够,没有发现问题。为了放大问题是用Thread中的sleep()方法 ...

    线程不安全问题

    当多线程并发访问同一个资源时,容易出现不安全问题                                                      有时候我们分析打印结果,发现没有问题,但是并不是真的没问题,可能我们经验不够,没有发现问题。为了放大问题是用Thread中的sleep()方法

    Thread.sleep(100);  //是当前线程睡眠100毫秒,让其他线程去抢资源,经常用来模拟网络延迟。放大线程中的问题。

    解决方法:

    (1)同步代码块

    (2)同步方法

    (3)锁机制(Lock)

    1.同步代码块

    synchronized( 同步锁){    //同步锁:同步监听对象/ 同步监听器 /互斥锁

    需要同步操作的代码

    }

    对象的同步锁只是一个概念,可以想象在对象上标记 一个锁

    java程序运行可以使用任何对象作为同步监听对象,但是一般我们将当前i并发访问的共同资源(多个线程同步共享的资源对象)作为同步监听对象。

    注意:在任何时候,只允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能在外面等


    2.同步方法

    使用synchronized修饰的方法叫做同步方法,保证该线程执行该方法的时候,其他线程只能等着。

    同步锁:1.非static方法,同步锁是this。2.static方法,使用当前方法所在类的字节码对象(Apple.class)是同步锁

    注意:

    synchronized不能修饰run方法,修饰之后,一个线程就执行了所有的功能,线程出现串行,相当于单线程。

    解决方法:将需要同步的 代码定义在一个新的方法中,并且该方法用synchronized修饰,再在run方法中调用该新的方法即可


    synchronized提高安全性,但是性能降低了,使用时尽量减小它的作用域。eg:stringBuffer和StringBuilder的区别,stringBuffer的方法全都加了synchronized修饰。

    类似的还有:ArrayList和vector  HashMap和Hashtable  一般选择使用性能比较高的。

    **单例模式-懒加载同步


    一般懒汉式



    优化懒汉式



    饿汉式

    结论:单例模式使用  饿汉式;简单粗暴。

    3.锁机制(Lock)

    Lock是一个接口,实现提供更广泛的锁定操作可以比使用synchronized获得方法和报表。

    锁是一种通过多个线程控制对共享资源的访问的工具。通常,一个锁提供对共享资源的独占访问:在一个时间只有一个线程可以获得锁和所有访问共享资源,需要先获得锁。




    谢谢阅读!
    展开全文
  • SimpleDateFormat线程不安全解决办法

    万次阅读 多人点赞 2017-06-10 15:15:23
    在此讨论一下SimpleDateFormat线程不安全问题,以及解决方法。 为什么SimpleDateFormat不安全? 直接上一个例子,日期格式化成字符串: package com.anjz.test.simpleDateFormat; import java.

    以前没有注意到SimpleDateFormat线程不安全的问题,写时间工具类,一般写成静态的成员变量,不知,此种写法的危险性!在此讨论一下SimpleDateFormat线程不安全问题,以及解决方法。

    为什么SimpleDateFormat不安全?

    直接上一个例子,日期格式化成字符串:

    package com.anjz.test.simpleDateFormat;
    
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试
     * @author ding.shuai
     * @date 2017年6月10日下午12:14:19
     */
    public class SimpleDateFormateTest2 {
    
    	public static void main(String[] args) {
    		final DateFormat df = new SimpleDateFormat("yyyyMMdd,HHmmss");
    		ExecutorService ts = Executors.newFixedThreadPool(100);
    		for (;;) {
    		    ts.execute(new Runnable() {		 
    		        @Override
    		        public void run() {
    		            try {
    		              //生成随机数,格式化日期
    		              String format =  df.format(new Date(Math.abs(new Random().nextLong())));
    		              System.out.println(format);
    		            } catch (Exception e) {
    		                e.printStackTrace();
    		                System.exit(1);
    		            }
    		        }
    		    });
    		}
    	}	
    }
    

    运行一段时间,出现下图的错误:

    再看一个例子,字符串解析成日期:
    package com.anjz.test.simpleDateFormat;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    /**
     * 测试
     * @author ding.shuai
     * @date 2017年6月10日下午12:38:02
     */
    public class SimpleDateFormateTest extends Thread{
    	
    	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    	
    	private String name;
    	private String dateStr;
    	
    	public SimpleDateFormateTest(String name,String dateStr) {
    		this.name = name;
    		this.dateStr = dateStr;
    	}
    
    	@Override
    	public void run() {
    		
    		try {
    			Date date = sdf.parse(dateStr);
    			System.out.println(name+": date:"+date);
    		} catch (ParseException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public static void main(String[] args) {
    		ExecutorService executorService = Executors.newFixedThreadPool(3);
    		
    		executorService.execute(new SimpleDateFormateTest("A", "2017-06-10"));
    		executorService.execute(new SimpleDateFormateTest("B", "2016-06-06"));
    		executorService.shutdown();
    	}
    }
    

    运行上述例子,可能每次结果都不一样,有时还会报各种错误,如下图,就是笔者运行报的几种错误(想出现下面几种错误,建议要多次运行)


    现在我们就分析一下,为什么出现上述原因:

    通过查看源码发现,原来SimpleDateFormat类内部有一个Calendar对象引用,它用来储存和这个SimpleDateFormat相关的日期信息,例如sdf.parse(dateStr),sdf.format(date) 诸如此类的方法参数传入的日期相关String,Date等等, 都是交由Calendar引用来储存的.这样就会导致一个问题,如果你的SimpleDateFormat是个static的, 那么多个thread 之间就会共享这个SimpleDateFormat, 同时也是共享这个Calendar引用。单例、多线程、又有成员变量(这个变量在方法中是可以修改的),这个场景是不是很像servlet,在高并发的情况下,容易出现幻读成员变量的现象,故说SimpleDateFormat是线程不安全的对象。

    ps:servlet因是线程不安全的,所以我们使用servlet的原则是不设置成员变量。

    SimpleDateFormat的parse方法:

    Calendar是用来承载字符串转化成日期对象的容器,calendar对象有个clear后set值的过程,高并发下,set值的过程,会出现把上次set值给覆盖的情况。


    SimpleDateFormat的format方法:

    我们传入的日期对象,会直接用Calendar承载,高并发下,Calendar承载的对象也会被覆盖。


    解决方法

    1、将SimpleDateFormat定义成局部变量。

    缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

    2、方法加同步锁synchronized,在同一时刻,只有一个线程可以执行类中的某个方法。

    缺点:性能较差,每次都要等待锁释放后其他线程才能进入。

    3、使用第三方库joda-time,由第三方考虑线程不安全的问题。(可以使用)

    4、使用ThreadLocal:每个线程拥有自己的SimpleDateFormat对象。(推荐使用)

    写一个工具类:

    package com.anjz.test.simpleDateFormat;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 时间工具类
     * @author ding.shuai
     * @date 2017年6月10日上午11:31:59
     */
    public class DateUtil {
    	
    	/**
    	 * 锁对象
    	 */
    	private static final Object lockObj = new Object(); 
    	
    	/**
    	 * 存放不同的日期模板格式的sdf的Map
    	 */
    	private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
    	
    	
    	/**
         * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
         * 
         * @param pattern
         * @return
         */
        private static SimpleDateFormat getSdf(final String pattern) {
            ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
    
            // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
            if (tl == null) {
                synchronized (lockObj) {
                    tl = sdfMap.get(pattern);
                    if (tl == null) {
                        // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                        System.out.println("put new sdf of pattern " + pattern + " to map");
    
                        // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                        tl = new ThreadLocal<SimpleDateFormat>() {
    
                            @Override
                            protected SimpleDateFormat initialValue() {
                                System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
                                return new SimpleDateFormat(pattern);
                            }
                        };
                        sdfMap.put(pattern, tl);
                    }
                }
            }
    
            return tl.get();
        }
        
        /**
         * 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
         * 如果新的线程中没有SimpleDateFormat,才会new一个
         * @param date
         * @param pattern
         * @return
         */
        public static String format(Date date, String pattern) {
            return getSdf(pattern).format(date);
        }
    
        public static Date parse(String dateStr, String pattern) throws ParseException {
            return getSdf(pattern).parse(dateStr);
        }
    }
    

    测试代码:

    package com.anjz.test.simpleDateFormat;
    
    import java.text.ParseException;
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 时间工具类测试
     * @author ding.shuai
     * @date 2017年6月10日上午11:44:01
     */
    public class DateUtilTest {
    
    	public static void main(String[] args) throws InterruptedException {
    		
    		Thread t1 = new Thread(){
    			@Override
    			public void run() {
    				DateUtil.format(new Date(), "yyyy-MM-dd");
    			};
    		};
    		
    		Thread t2 = new Thread(){
    			@Override
    			public void run() {
    				DateUtil.format(new Date(), "yyyy-MM-dd");
    			};
    		};
    		
    		Thread t3 = new Thread(){
    			@Override
    			public void run() {
    				DateUtil.format(new Date(), "yyyy-MM-dd");
    			};
    		};
    		
    		Thread t4 = new Thread(){
    			@Override
    			public void run() {
    				try{
    					DateUtil.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
    				}catch(ParseException e){
    					e.printStackTrace();
    				}
    				
    			};
    		};
    		
    		Thread t5 = new Thread(){
    			@Override
    			public void run() {
    				try{
    					DateUtil.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
    				}catch(ParseException e){
    					e.printStackTrace();
    				}
    				
    			};
    		};
    		
    		
    		System.out.println("单线程执行:");
    		 ExecutorService exec1 = Executors.newFixedThreadPool(1);
    		 exec1.execute(t1);
    		 exec1.execute(t2);
    		 exec1.execute(t3);
    		 exec1.execute(t4);
    		 exec1.execute(t5);
    		 exec1.shutdown();
    		 
    		 Thread.sleep(1000);
    		 
    		 System.out.println("双线程执行:");
    		 ExecutorService exec2 = Executors.newFixedThreadPool(2);
    		 exec2.execute(t1);
    		 exec2.execute(t2);
    		 exec2.execute(t3);
    		 exec2.execute(t4);
    		 exec2.execute(t5);
    		 exec2.shutdown();
    	}
    }
    

    执行结果:

    单线程执行:
    put new sdf of pattern yyyy-MM-dd to map
    thread: Thread[pool-1-thread-1,5,main] init pattern: yyyy-MM-dd
    put new sdf of pattern yyyy-MM-dd HH:mm:ss to map
    thread: Thread[pool-1-thread-1,5,main] init pattern: yyyy-MM-dd HH:mm:ss
    双线程执行:
    thread: Thread[pool-2-thread-1,5,main] init pattern: yyyy-MM-dd
    thread: Thread[pool-2-thread-2,5,main] init pattern: yyyy-MM-dd
    thread: Thread[pool-2-thread-1,5,main] init pattern: yyyy-MM-dd HH:mm:ss
    thread: Thread[pool-2-thread-2,5,main] init pattern: yyyy-MM-dd HH:mm:ss
    

    从结果我们可以看出:

    1)1个线程执行5个任务,new了两个SimpleDateFormat对象

    2)2个线程执行5个任务,new了四个SimpleDateFormat对象,每个线程拥有两个格式为“yyyy-MM-dd”、“yyyy-MM-dd HH:mm:ss”的对象,线程之间没有共享SimpleDateFormat对象,对于每个线程,都是线性执行,也不会出现共享Calendar的现象。故完美解决SimpleDateFormat线程不安全的问题。

    如果使用第一种方式,5个任务的执行,肯定需要new出5个SimpleDateFormat,对于单个线程没有复用的概念。但使用ThreadLocal,对于单个线程,相同格式是可以复用SimpleDateFormat对象,所以最后一种方式,既可以保证线程安全,又可以不耗费系统太多资源,其实这种思想和web中request,response对象是一样的,都是通过线程隔离,每个线程维护一份自己的对象来保证线程安全。


    最后附上阿里开发手册中一段关于SimpleDateFormat的规范:

    【强制】SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。

    正例:注意线程安全,使用DateUtils。亦推荐如下处理:

    private static final ThreadLocal<DateFormat> df = newThreadLocal<DateFormat>() {     
           @Override      
           protected DateFormatinitialValue() {
            return newSimpleDateFormat("yyyy-MM-dd");     
          } 
     };  

    说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。

    展开全文
  • 解决 ArrayList 的线程不安全

    千次阅读 2020-04-12 10:54:22
    文章目录前文为什么说 ArrayList 是线程安全的?故障现象导致原因解决方法优化速度 ...看过我之前写的深入理解 ArrayList 的朋友们应该都知道 ArrayList 是线程安全的,但是为什么说 ArrayList 是...
  • 线程不安全的原因及其解决

    千次阅读 2020-06-11 23:18:44
    一、什么是线程安全和线程安全 定义:线程安全:多线程并发执行某个代码时,产生了逻辑上的错误,结果和预期值相同 线程安全是指多线程执行时没有产生逻辑错误,结果和预期值相同 二、线程安全产生的原因 ...
  • 解决线程安全问题

    千次阅读 2021-02-15 16:25:30
    解决线程安全问题 1.synchronized 关键字-监视器锁monitor lock synchronized的底层是使用操作系统的mutex lock实现的。 当线程释放锁时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中; 当线程获取锁...
  • 如何解决线程安全问题

    万次阅读 2021-03-11 11:14:03
    如何解决线程安全问题 怎么解决线程的安全问题呢? 基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式,即在同一时刻只有一个线程操作临界资源,操作完了才能让其他线程进行操作,也称作同步...
  • 解决ArrayList线程不安全

    万次阅读 2012-03-01 19:29:35
    前些天做项目时,程序出现意外的问题,经后来分析是使用ArrayList这个线程不安全的方法导致 解决这个问题通常有两种方法(个人认为) 一:使用synchronized关键字,这个大家应该都很熟悉了,解释了; 二:使用...
  • 线程安全 & 线程安全函数 & 线程安全函数 线程安全 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程能进行访问直到该线程读取完,其他线程才可使用。不会出现数据...
  • Spring解决单例bean线程不安全问题

    千次阅读 多人点赞 2019-05-27 17:32:11
    首先我们应该知道线程安全问题一般发生在成员变量上,这是为什么啦? 因为成员变量是存放在堆内存中...下面我们就来研究下如何解决Spring中单例Bean的线程安全问题 @RestController //@Scope("prototype") publ...
  • 如果你的代码所在的进程中有多个线程在同时运行,而这些线可能会同时运行这段代码。...很显然可以将集合分为两组,线程安全和非线程安全,Vectore是用同步方法来是实现线程安全的而和他相似的ArrayList是线程安全的。
  • HashMap线程不安全原因及解决

    千次阅读 2020-03-27 15:09:58
    HashMap线程不安全的原因: 1、put的时候导致的多线程数据一致。 这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的...
  • 线程同步:解决线程安全问题

    千次阅读 2018-02-09 18:50:45
    线程安全问题,是由于多个线程在访问共享的数据(共享的资源),并且操作共享数据的语句不止一条。那么这样在多条操作共享数据的之间线程就可能发生切换,从而引发线程安全问题。例如如下情况:public class ...
  • 1.ArrayList 线程不安全验证 验证Demo: /** * 集合类ArrayList线程不安全验证 * * @author wangjie * @version V1.0 * @date 2019/12/17 */ public class ContainerNotSafe { public static void main...
  • boost json线程不安全及其解决办法

    千次阅读 2016-11-23 17:47:01
    项目中用多线程解析json数据,其中用到了boost 的json parser,但是近来发现问题会导致程序出core,最终定位原因是 boost json parser中用到ptree底层依赖boost::spirit,是线程不安全的,从而导致程序出core。解决...
  • 今天,给大家带来一篇Java SimpleDateFormat在多线程环境下不安全的文章,Java SimpleDateFormat 是线程不安全的,当在多线程环境下使用一个DateFormat的时候是有问题的,如下面的例子 package com.lya.date; import...
  • 1.什么是线程安全问题 就是 多线程环境中 , 且存在数据共享 , 一个线程访问的共享 数据被其他线程修改了, 那么就发生了线程安全问题 , 整个访问过程中 , 无一共享的数据被其他线程修改了 就是线程安全的 程序中如果...
  • 一、什么是线程安全?...二、解决线程安全 先举个线程安全的示例: 场景:商品抢购(抢购就是多线程,并发请求) 示例:假设在抢购场景中,我们一共只有10个商品,在最后一刻,我们已经卖出了9个商品
  • ArrayList线程不安全举例及解决

    千次阅读 2019-09-05 09:03:33
    这两个都是接口List下的一个实现,用法都一样,但用的场所的有点不同,ArrayList适合于进行大量的随机访问的情况下使用,LinkedList适合在表中进行插入、删除时使用,二者都是非线程安全解决方法同上(为了避免...
  • java中哪些集合是线程安全的,哪些是线程安全的

    万次阅读 多人点赞 2019-05-09 11:41:42
    线程安全和线程安全的集合3. 如何综合考虑线程安全和效率低的问题 1. 常见集合 这里自己总结了一些比较常见的java集合,对于这些集合的特点和区别后期博客中会进行总结介绍: 2.什么叫“集合是线程安全的” ...
  • SimpleDateFormat 的线程安全问题与 ThreadLocal http://blog.jrwang.me/2016/java-simpledateformat-multithread-threadlocal/ 解读: SimpleDateFormat里面有个变量,多线程可能会公用,造成冲突,所以线程安全...
  • LinkedList多线程不安全解决办法

    千次阅读 2014-06-06 10:06:15
    LinkedList是线程不安全的 在多线程下遍历LinkedList,经常会抛出如下异常: java.util.ConcurrentModificationException java.util.LinkedList$ListItr.checkForComodification(Unknown Source) java.util....
  • SimpleDateFormat线程不安全解决办法

    千次阅读 2018-08-15 10:19:59
    使用ThreadLocal,具体关于ThreadLocal,这里做介绍。直接上代码 private static ThreadLocal&lt;SimpleDateFormat&gt; local = new ThreadLocal&lt;SimpleDateFormat&gt;(); public static...
  • 众所周知,hashmap线程安全而hashtable线程安全。最近在看并发编程就稍微研究了一下。先看一段JAVAAPI中对hashmap的介绍:*注意,此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改...
  • 1.线程安全性问题是什么?使用什么场景的线程才会出现安全性问题?我们用程序来举例 这是Runable线程类 public class Runable implements Runnable{ int i = 100; @Override public void run() { // ...
  • 同步锁-线程安全问题解决方案

    万次阅读 多人点赞 2021-03-21 15:12:05
    经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象. 上节笔记点这里-进程与线程笔记 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件: 在多线程程序中 +...
  • 什么是线程安全和线程安全

    千次阅读 2019-12-02 16:57:42
    1、线程安全: 指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,存在执行程序时出现意外结果。 2、线程安全: 是指提供加锁机制保护,有可能出现多个线程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 434,052
精华内容 173,620
关键字:

怎么解决线程不安全