多线程并发问题以及单例设计模式与线程安全以及同步方法和同步代码块

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ilikejj0/article/details/82686600

线程安全和非线程安全

在操作系统中,线程是不拥有资源的,进程拥有资源。线程是由进程创建的,一个进程可以创建多个线程,这些线程共享进程中的资源。当多个线程同时操作一个变量时,这个时候就可能会造成数据的不一致性,此时就是线程不安全。

JVM有主内存(Main Memory)和工作内存(Working Memory),主内存就是平时所说的java堆内存,存放程序中所有的类实例、静态数据等变量,是线程共享的,而工作内存中存放的是从主内存中拷贝过来的变量以及访问方法所取得的局部变量,是每个线程独立所有的,其他线程不能访问。

每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完成,操作完成后再将变量写回主内存;

各个线程都从主内存中获取数据,线程之间数据是不可见的;打个比方:主内存变量A原始值为1,线程1从主内存取出变量A,修改A的值为2,在线程1未将变量A写回主内存的时候,线程2拿到变量A的值仍然为1;

多线程并发不安全主要就是因为资源的共享,如果没有资源共享,那么此时多线程并发是安全的。

i++与++i的线程安全问题

i++和++i的线程安全分为两种情况:

1、如果i是局部变量(在方法里定义的),那么是线程安全的。因为局部变量是线程私有的,别的线程访问不到,其实也可以说没有线程安不安全之说,因为别的线程对他造不成影响。

2、如果i是全局变量(类的成员变量),那么是线程不安全的。因为如果是全局变量的话,同一进程中的不同线程都有可能访问到。

设计模式

前辈们总结出来的一套合理切高效的设计思路,理解和使用模式,能够在很大程度上减少时间的浪费以及提高代码的可复用性和扩展性。

单例设计模式:只有一个实例化对象
单例设计模式适用场景就是:这个类在这个项目中使用频率十分高,如果每次使用都去new一个对象,此时会造成很大的内存开销。

单例模式分为懒汉式(你需要的时候给你创建)和饿汉式(不管你需不需要我都先创建好)。
单例模式:构造方法都是私有的,避免外界访问。
懒汉式:有一个私有静态的成员变量和供外界访问的一个静态方法,在静态方法中进行判断,如果私有静态的成员变量是空,那么还没有创建对象,开始创建对象,并将其赋值给私有静态的成员变量。如果不为空,直接返回私有静态成员变量。
饿汉式:有一个私有静态的成员变量和供外界访问的一个静态方法,私有静态的成员变量是已经初始化的,即已经创建好对象,在静态方法中只是将这个创建好的对象返回出去。

使用:
当我们在getInstance()方法中传参的时候需要使用懒汉式,饿汉式也有一个弊端,就是我们需要获取一个全局成员变量的时候,但是得到的却是一个实例,会导致加载时间过长。

懒汉式是线程不安全的,而饿汉式是线程安全的。
原因:当多个线程并发的情况下执行getInstance()的方法的时候,在懒汉式情况下会出现这种情况:
线程一:进入方法getInstance()此时判断为null,开始进行初始化实例对象;
线程二:可能在线程一实例化对象还未创建完成的时候进来,此刻,进行判断依然为null,然后线程二又去实例化对象了,这样下来不就出现了两个对象了,显然这不是想要的结果。

[单例模式与线程安全] ##(https://blog.csdn.net/wangheng_2017/article/details/63254018)

解决办法:
需要使用synchronied来实现线程同步,当一个线程获得锁的时候其他线程就只能在外等着 ,在getInstance()方法上加锁。但是也有弊端:会大大降低性能,同步方法要比一般方法慢很多,如果频繁的调用getInstance()方法,会造成性能的累计消耗。

public synchronized static Demo getDemo(){
        if(d==null){
            d=new Demo();       
        }
        return d;
    }

使用双重校验锁:
在进行判断当前对象是否为空的时候,使用同步代码块。这个时候,但是当线程1和线程2同时判断(d==null)的时候就会一起进入到同步代码块当中。此时在同步代码块中要在进行判断是否为空,然后为空再创建对象。不然还是会创建出来两个对象。还要使用关键字volatile来保证禁止指令重排优化了。

public class Demo {
    private static volatile Demo d;

    private Demo(){};

    public static Demo getDemo(){
        if(d==null){
            synchronized(Demo.class){
                if(d==null){
                    d=new Demo();       
                }               
            }           
        }
        return d;
    }
}

同步方法

[详解同步方法] ##(http://topmanopensource.iteye.com/blog/1738178)

所有非静态同步方法使用的都是同一把锁,——->实例对象本身,即this
所有的静态同步方法使用的也是同一把锁,——–>类对象本身,即class.
非静态同步方法和静态同步方法使用的是两个不同的锁对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
对于同步块,由于其锁是可以选择的,所以只有使用同一把锁的同步块之间才有着竞态条件,

同步的例子

猜你喜欢

转载自blog.csdn.net/ilikejj0/article/details/82686600