线程锁(synchronized与volatile)

线程锁(synchronized与volatile)
简单地对synchronized和volatile两种锁进行总结,以便以后查找方便,希望大家提出宝贵建议,以便日后进一步完善。
1.synchronized的用法:
1)修饰方法的三种用法:
a)public class Test1{
private Object o =new Object();
public void invoke(){
synchronized(o){
//需要执行的代码
}
}
}
b)public class Test2{
public void invoke(){
synchronized(this){
//需要执行的代码
}
}
}
c)public class Test3{
public synchronized void invoke(){
//需要执行的代码
}
}
综述:三种方法效果一样,由此可以发现synchronized锁的不是方法,而是锁的对象。
2)synchronized用static修饰时
public class Test4 {
private intcount= 10;
public synchronized static void invoke(){
//需要执行的代码
}
public static void invoke2(){
synchronized(Test4.class){
//需要执行的代码
}
}
}
注意:两个静态方法效果一样,synchronized修饰静态方法时,锁的时Test类,而不是Testnew出的对象。与synchronized修饰的非京台方法相比,静态的是锁的一个类,而非静态的锁的是一个具体的对象。
问题总结:
synchronized修饰静态方法,下面简为静态的,修饰非静态方法简称为非静态的。
情形一:当创建两个非静态方法对象,同时有两个线程去跑这两个对象,如果两个对象公用一个资源就会出现线程安全问题。而静态的就不会出现线程安全问题;这是由于静态的是锁的类,而非静态的锁的是该类创建出来的对象,不同的对象就是不同的锁,所以非静态方法只能锁住自己的对象。而静态的能锁住该类创建的所有对象。代码如下:

package com.example.girls.testThread;

public class TestThread implements Runnable {
    private static int count = 10;
    @Override
    public  void run() {
        invokeTest();

    }

    public synchronized static void invokeTest(){
        count--;
        System.out.println(Thread.currentThread().getName() +":count ="+ count);
    }

    public static void main(String[] args) {
        TestThread t = new TestThread();
        TestThread t2 = new TestThread();
        for (int i = 0; i<5; i++){
            new Thread(t,"THREAD = "+i ).start();
            new Thread(t2,"THREAD2 = "+i ).start();
        }
    }
}

3)同一个欸中synchronized的修饰的同步方法和非同步方法,能否同时被调用
set方法被synchronized修饰被调用时,需要获得锁,而get方法则不需要锁;因此,在set被调用时,而get方法不需要获得锁,所以在set调用时,同时也可以调用get方法。这样在set复制的同时,调用get方法,此时有可能set还没有赋值完成,get获得值为0,就会出现脏读的情况,

package com.example.girls.testThread;

public class TestThread2 {
    private int count;
    private String name;

    public synchronized void set(int count, String name){
        this.name = name;
        try {
           /* 加上睡眠时间为了模拟实际中的高并发,
           为了证明获取和写入的不同步,
           从而说明在一个类中加锁的和不加锁的方法能够同时被调用
           */
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        this.count = count;
    }

    public int get(String name){
        return this.count;
    }

    public static void main(String[] args) {
        TestThread2  t = new TestThread2();
        new Thread(()->t.set(100000,"zhangsan")).start();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //此处为了证明同步方法和非同步方法能同时调用
        System.out.println("1秒后:"+t.get("zhangsan"));
        try {
            Thread.sleep(3000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("3秒后:"+t.get("zhangsan"));
    }
}

4)重入锁
在同一个类中两个同步方法,其中一个方法调用另一个同步方法,两个方法都能执行;调用的同步方法会再去找对象的锁,在原来锁的基础上加一。
子类的synchronized方法可以调用父类的synchronized方法。

package com.example.girls.testThread;

import javax.persistence.criteria.CriteriaBuilder;

public class TestDoubleLock {
    public synchronized void invoke(){
        System.out.println("invoke执行");
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        invoke2();
    }
    public synchronized void invoke2(){
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("invoke2执行");
    }

    public static void main(String[] args) {
        TestDoubleLock t = new TestDoubleLock();
        new Thread(()->t.invoke()).start();
    }
}

4)synchronized的锁抛异常就会释放锁
解决方案:加上try...catch
2 volatile的用法
volatile修饰变量,使得在所有线程中该变量可见,修改变量可以同步到正在运行中的线程中。
volatile对变量读操作进行了同步,对写操作没有惊醒控制,因此,在多线程进行写操作时会出现线程安全问题。

package com.example.girls.testThread;

import java.util.ArrayList;
import java.util.List;

public class TestVolatile {
    private volatile int count = 18;

    public void getCount(){
        for (int i = 0; i<1000;i++){
            try {
                Thread.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            this.count ++;
        }
    }
    public void setCount(){
        try {
            Thread.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        this.count ++;
    }

    public static void main(String[] args) {
        TestVolatile t = new TestVolatile();
        List<Thread> threads = new ArrayList<>();

        for (int i=0; i<2;i++){
            threads.add(new Thread(()->t.getCount()));
        }
        /**
         * 创建两个线程对count,同时进行修改
         * volatile只保证同步读,即变量的可见性;
         * 卫队其写的操作进行控制,会出现两个线程同时对count的同一值进行操作,
         * 造成最后结果出现小于2018
        */
        threads.forEach((o)->o.start());
        threads.forEach((o)->{
            try {
                //等待线程结束后再执行main方法
                o.join();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        System.out.println("count = "+t.count);
    }
}

猜你喜欢

转载自www.cnblogs.com/myworldfordata/p/9038144.html