线程安全2—synchronized关键字的使用。

当使用多线程访问同一个资源时,非常容易出现线程安全的问题,例如,当多个线程同时对一个数据进行修改时,会导致某些线程对数据的修改丢失。
线程安全的前提:
1.保证原子性
2.保证顺序性
3.保证可见性
为了保证以上能够完成,采用了同步机制来解决问题。
一.synchronized关键字-监视器锁
1.原理
ynchronized的底层是使用操作系统的mutexlock实现的。
*当线程释放锁时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中
*当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量
*synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。
2.synchronized关键字的用法
在Java中,每一个对象都有一个对象锁与之关联,该锁表明对象在任何时候只允许被一个线程所拥有,当一个线程在调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,在这个线程执行时,其他线程不得访问,执行结束后,才释放锁,其他线程才能去抢夺这把锁,然后执行代码。这就保证了不会发生多个线程抢夺共享资源的问题,synchronized主要有两种使用 方法

第一种:synchronized方法,在方法申明前加入synchronized关键字。

public synchronized static void method1(){...}//修饰静态方法

public synchronized void method2(){...}//修饰非静态方法

第二种:synchronized块,将需要加同步锁的代码用synchronized块包裹住。

public void method3(){
    synchronized(obj){//括号里申明指定上锁的对象
    .......//do something
    }
}

3.弄清楚synchronized锁住的对象是哪个
synchronized锁住对象的所有同步的地方都会同步互斥。
1.修饰块
A.synchronized(this){//同步代码块}

锁住的是this所指代的对象,当所有任意一个线程在执行这段同步代码块时,其他任意访问this所代表的对象的线程都会被阻塞,直到这段同步代码块执行完成,才会释放锁,其他线程才能操作this对象

public class T {
    public  void method1(){
        synchronized (this){
        System.out.println(Thread.currentThread().getName());
        while (true){}
        }
    }
    public void method2(){
        synchronized (this){
        System.out.println(Thread.currentThread().getName());
        while (true){}
        }
    }
    public static void main(String[] args) {
        T t1=new T();
        T t2=new T();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.method1();//这里this指代是t1
            }
        },"线程1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.method2();
            }
        },"线程2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t2.method1();//这里this指代的是t2
            }
        },"线程3").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                t2.method2();
            }
        },"线程4").start();
    }

}

因为锁的是当前this的对象,只会对同一个对象起互斥作用,不是同一个对象synchronized就不起作用,所以所以输出结果只能是"线程1"和"线程2"中的一个,"线程3"和"线程4"中的一个。

线程1
线程3

Process finished with exit code -1

B.synchronized(object){//同步代码块}
同上锁住的是obj对象,这样当某个线程正在执行这段代码时,其他线程只是会被obj阻塞,但是仍然可能访问T类对象的其他方法

public class T {
 public static void method1(){
     System.out.println(Thread.currentThread().getName());
     while (true){}
 }
    public static void main(String[] args) {
            T t1=new T();
            T t2=new T();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (t1) {
                    method1();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (t1) {
                    method1();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (t2){
                    method1();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (t2){
                    method1();
                }
            }
        }).start();
    }

}

因为只会对synchronized()里的对象堵塞,所以结果是

线程1
线程4

Process finished with exit code -1

C.synchronized(T.class){//同步代码块}
锁住T的所有对象,只要任意一个线程在访问T,其他线程在访问时都会被阻塞住,包括所有实例对象的方法访问和T的静态成员或静态方法,这种方式效率比较低,在不需要锁住所有实例,所有方法的场景,不需要使用这种方式,在多线程资源竞争恶劣的情况下,会大大降低多线程的效率。

public class T {
    public static void method1(){
        System.out.println(Thread.currentThread().getName());
        while (true){}
    }
    public static void method2(){
        System.out.println(Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        T t1=new T();
        T t2=new T();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (T.class) {
                    method1();
                }
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (T.class) {
                    method2();
                }
            }
        },"线程2").start();
    }

}

因为锁住的是T类这个所有对象,所以结果是

线程1

Process finished with exit code -1

2.修饰方法
A.修饰静态方法,锁住的是这个类的所有对象。

public class T {
    public synchronized static void method1(){
        System.out.println(Thread.currentThread().getName());
        while(true){

        }
    }
    public synchronized static void method2(){
        System.out.println(Thread.currentThread().getName());
        while(true){

        }
    }
 public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                method1();
            }
        },"线程1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                method2();
            }
        },"线程2").start();
    }
}   

尽管两个线程执行的不是同一个T类里的同一方法,但此时synchronized修饰的是静态方法,锁的是这个类的所有对象,所以输出结果只能是两个中的一个

线程2

Process finished with exit code -1

B.修饰非静态方法,锁住的是正在执行这个同步方法的对象

public class T {
    public synchronized  void method1(){
        System.out.println(Thread.currentThread().getName());
        while(true){}
    }
    public synchronized  void method2(){
        System.out.println(Thread.currentThread().getName());
        while(true){}
    }
    public synchronized void method3(){}


    public static void main(String[] args) {
        T t1=new T();
        T t2=new T();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.method1();
            }
        },"线程1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.method2();
            }
        },"线程2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t2.method1();
            }
        },"线程3").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                t2.method2();
            }
        },"线程4").start();
    }
}

因为锁的是执行当前同步方法的对象。 并不会对其他的对象起互斥效果,所以输出结果只能是"线程1"和"线程2"中的一个,"线程3"和"线程4"中的一个。

线程1
线程4

Process finished with exit code -1

从以上的示例中我们可以看出来synchronized(this){}和synchronized修饰非静态方法是等效的,锁的是线程访问的同一对象;synchronized(T.class){}与synchronized修饰静态方法是等效的,锁的是T类的所有对象。而synchronized(object){}使用更加灵活,锁的是自己指定的对象。

你可能感兴趣的:(线程安全2—synchronized关键字的使用。)