ThreadLocal 和 synchornized 两者的区别

时间:2022-11-27 11:57:29


package com.itheima.hello.runnable;

import java.util.Random;

public class ThreadDemo1 implements Runnable {
    private int count = 0;
    Student student = new Student();
    private Object studentLock = new Object();
    private Object countLock = new Object();

    public static void main(String[] agrs) {
        ThreadDemo1 td = new ThreadDemo1();
        Thread t1 = new Thread(td, "a");
        Thread t2 = new Thread(td, "b");
        t1.start();
        t2.start();

    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    public void run() {
        accessStudent();
    }

    public void accessStudent() {
        long startTime = System.currentTimeMillis();
        String currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " is running!");
        // System.out.println("first  read age is:"+this.student.getAge());
        /* 同步代码块 */
        synchronized (countLock) {
            try {
                this.count++;
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            System.out.println("thread " + currentThreadName + " read count:"
                    + this.count);
        }
        synchronized (studentLock) {
            Random random = new Random();
            int age = random.nextInt(100);
            System.out.println("thread " + currentThreadName + " set age to:"
                    + age);
            synchronized (this) {
                this.student.setAge(age);
                System.out.println("thread " + currentThreadName
                        + " first  read age is:" + this.student.getAge());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
         System.out.println("thread " + currentThreadName+" second read age is:" + this.student.getAge());
        }
        long endTime = System.currentTimeMillis();
        long spendTime = endTime - startTime;
        System.out.println(Thread.currentThread().getName()+"花费时间:" + spendTime + "毫秒");
    }

    public class Student {
        private int age = 0;

        public int getAge() {
            return this.age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}
/*
 * 首次读取和second读取不同,这样是错误的,那么我们需要实现synchornized synchronized 的同步代码块
 * 使用同步代码块的话,需要对方法进行同步的代价是非常昂贵的
 * 。特别是当被同步的方法执行一个冗长的操作。这个方法执行会花费很长的时间,对这样的方法进行同步可能会使系统性能成数量级的下降。 我们可以将同步细化一下
 * 使用锁的方式
 */



Threadlocal   ​​javascript:void(0)​​


blog 2            ​​http://www.iteye.com/topic/81936​



ThreadLocal其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。 




JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并



不是一个Thread,而是Thread的 ​​局部变量​




protected Object initialValue()



返回该线程 ​​局部变量​​​的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在 ​​​线程​​第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。



void set(Object value)



public void remove()



将当前线程 ​​局部变量​​​的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的 ​​​局部变量​​​将自动被 ​​​垃圾回收​​,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。



值得一提的是,在JDK5.0中,ThreadLocal已经支持 ​​泛型​​,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。



ThreadLocal是如何做到为每一个线程维护 ​​变量​​的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。



threadlocal 为 jdk1.2 提供解决多线程并发的一种新的思路


jdk1.5 新增 remove()移除当前局部变量的值


jdk5.0 threadlocal 已经支持繁星 threadloca<T>对应的方法也进行了修改 void set(T value)、T get()以及T initialValue()。



比较


ThreadLocal和线程同步机制都是为了解决多线程中相同 ​​变量​​的访问冲突问题。




帮助理解


个人理解,我们现在软件经常会分层,比如MVC ,类似这种横向分层,而ThreadLocal会提供一种方式,方便的在同一个线程范围内,提供一个存储空间,供我们使用,实现纵向的存储结构,便于我们在同一个线程范围内,随时取得我们在另外一个层面存放的数据。





用法三:在Runnable中创建ThreadLocal



 还有一种用法是在线程类内部创建ThreadLocal,基本步骤如下:



1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。  
2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。  
3、在ThreadDemo类的run()方法中,通过调用getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。  

Synchronized还是ThreadLocal? 
ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用 ThreadLocal会获得更大的性能。虽然使用ThreadLocal会带来更多的内存开销,但这点开销是微不足道的。因为保存在 ThreadLocal中的对象,通常都是比较小的对象。另外使用ThreadLocal不能使用原子类型,只能使用Object类型。 ThreadLocal的使用比synchronized要简单得多。 
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区 别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本, 使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通 信时能够获得数据共享。 
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。 
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。