1.数据不可变:由于一切不可变的对象是线程安全的,所以对于一些对象自身就是线程安全的:final修饰的基础数据类型。字符串类型,以及final修饰的引用数据类型不是线程安全的;这些不可变的对象成为(immutable)。

2.互斥同步:互斥是方法,同步是目的;同步是指:多个线程并发的访问共享的数据时,保证共享的数据在同一时间内只能被一个线程使用。

	最基本的互斥同步的手段是使用synchronized字段实现;
    
    缺点:线程的阻塞和唤醒带来的性能开销(线程切态)。即线程阻塞在那里等着别人唤醒,所以互斥同步也叫做阻塞同步;
    
    从解决问题的方式看:互斥同步是悲观并发策略(一个存在着可能的事件,你总认为不能,所以而不去尝试。这就是被人管的并发策			略);
    
    这个操作由于维护锁计数器,检查是否被线程阻塞,所以需要大量的开销。
    

3.非阻塞同步(冲突检测的乐观并发策略):

不管有无风险,先操作,如果无冲突,我直接可以使用。如果有其他的线程我就补偿策略:不断的重试!也称为自旋!

所以放线程比较少的时候就可以使用冲突检测的乐观并发策略,此时性能比较高,但是当其他线程一直不放资源,那么自旋也会好大量的资源。

4.无同步方案: 场景:需要共享数据没错,但是这些数据可以在本地计算得到结果,但是不放回主存,这样就不会产生问题;


public class NoSynchronized {
    private static int number = 0;
    //使用ThreadLocal类实现,变量的线程中存储
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    //使用之前将这个对象包住一下

    //无同步方案,number为共享的资源,线程t1,t2想在自己的线程内部对number各增加1000次,
    //使用将共享的资源,放置到自己的线程内存中的方式避免安全问题;
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            threadLocal.set(number);//相当于把主内存的0保存到了本地的线程内存中
            for (int i = 0; i < 1000; i++) {
                threadLocal.set(threadLocal.get() + 1);
                System.out.println("t1____:" + threadLocal.get());
            }
        });
        Thread t2 = new Thread(()->{
            threadLocal.set(number);
            for (int i = 0; i < 1000; i++) {
                threadLocal.set(threadLocal.get() + 1);
                System.out.println("t2____:" + threadLocal.get());
            }
        });
        t1.start();
        t2.start();
        //考虑有三点问题:
        //这个资源是否要使用;资源是否被修改;主存中的值是否需要改变
        //在此处,很明显主存中的值是不需要修改的
        //未使用ThreadLocal修饰之前,最后的打印的结果可能是1999,这不是想要的结果


    }
}