synchronized锁

为什么使用synchronized锁

如果多个线程对同一变量,并发访问修改,没有做同步处理最终就会导致出现脏读的问题。

非线程安全的变量

方法内的变量为线程安全的变量:jvm方法调用在操作数栈中,而操作数栈是每个线程独有的,线程间的栈是不共享的,所以方法内的变量是每个线程独有的,所以不会出现脏读(题外话:在单个线程间栈内创建数据如果存在值相同的,就指向同一个地址,就是栈共享)。
实例变量为非线程安全的变量:实例变量在实例化后将在堆上分配,而堆是线程间共享的,那么就会出现非线程安全的问题。

synchronized锁重入

  1. 可重入锁概念就是自己可以再次获取自己的内部锁。
public class MyService {

    public synchronized void method1() {
        System.out.println("method1");
        method2();
    }
    
    public synchronized void method2() {
        System.out.println("method2");
        method3();
    }

    public synchronized void method3() {
        System.out.println("method3");
    }
}

public class ServiceThread extends Thread {

    @Override
    public void run() {
        MyService ms = new MyService();
        ms.method1();
    }
}

public class Test {
    
    public static void main(String[] args) {
        ServiceThread st = new ServiceThread();
        st.start();
    }
}

结果:

[Console output redirected to file:D:\console.txt]
method1
method2
method3

  1. 可重入锁支持父子继承关系中
public class Father {
    public synchronized void execute1() {
        System.out.println("Father execute1");
    }
}

public class Children extends Father{
    public synchronized void execute2() {
        System.out.println("Children execute2");
        execute1();
    }
}

public class ServiceThread extends Thread {
    @Override
    public void run() {
        Children c = new Children();
        c.execute2();
    }
}

public class Test {
    
    public static void main(String[] args) {
        ServiceThread st = new ServiceThread();
        st.start();
    }
}

结果:

[Console output redirected to file:D:\console.txt]
Children execute2
Father execute1

异常,锁自动释放

public class Children {

    public synchronized void execute2() {
        System.out.println("Children execute2");
        
        int i = 3 / 0;
        
        System.out.println("i = " + i);
    }
}

public class ServiceThread extends Thread {

    @Override
    public void run() {
        Children c = new Children();
        c.execute2();
    }
}

public class Test {
    
    public static void main(String[] args) {
        ServiceThread st = new ServiceThread();
        st.start();
    }
}

结果:

[Console output redirected to file:D:\console.txt]
Children execute2
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
    at cn.spy.thread.test.Children.execute2(Children.java:8)
    at cn.spy.thread.test.ServiceThread.run(ServiceThread.java:8)

synchronized同步代码块

synchronized方法对当前对象进行加锁,synchronized代码块是对某一个对象进行加锁。
1. 同步代码块和同步方法的区别
同步方法的特点就是只要是当前对象的同步方法,当前线程没执行完,其他线程只能等着。而同步代码块则是对于不同对象的同步代码块,线程1执行同步代码块1时,线程2也可以执行同步代码块2,这样效率会有所提升。而在我看来实际上两种方式只是细粒度上进一步得到了提升。同步代码块可以进行更加细微的操作。
2. synchronized(this)锁定当前对象

public class MyTask {

    public void execute() {
        synchronized (this) {
            for (int i = 0 ; i < 3 ; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }
}

public class MyThreadA extends Thread{

    private MyTask myTask;

    public MyThreadA(MyTask myTask) {
        this.myTask = myTask;
    }

    @Override
    public void run() {
        myTask.execute();
    }
}

public class MyThreadB extends Thread{

    private MyTask myTask;

    public MyThreadB(MyTask myTask) {
        this.myTask = myTask;
    }

    @Override
    public void run() {
        myTask.execute();
    }
}

public class MyTest {

    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        MyThreadA mtA = new MyThreadA(myTask);
        mtA.setName("MyThreadA");
        MyThreadB mtB = new MyThreadB(myTask);
        mtB.setName("MyThreadB");
        mtA.start();
        mtB.start();
    }
}

结果:

MyThreadA 0
MyThreadA 1
MyThreadA 2
MyThreadB 0
MyThreadB 1
MyThreadB 2

静态同步synchronized

synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。

public class MyTask {

    public synchronized void execute() {
        System.out.println(Thread.currentThread().getName() + " execute start");
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName() + " execute end");
    }
    
    public synchronized void print() {
        System.out.println(Thread.currentThread().getName() + " print start");
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName() + " print end");
    }
}

public class MyThreadA extends Thread{

    private MyTask myTask;

    public MyThreadA(MyTask myTask) {
        this.myTask = myTask;
    }

    @Override
    public void run() {
        myTask.execute();
    }
}

public class MyThreadB extends Thread{

    private MyTask myTask;

    public MyThreadB(MyTask myTask) {
        this.myTask = myTask;
    }

    @Override
    public void run() {
        myTask.print();
    }
}

public class MyTest {

    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        MyThreadA mtA = new MyThreadA(myTask);
        mtA.setName("MyThreadA");
        MyThreadB mtB = new MyThreadB(myTask);
        mtB.setName("MyThreadB");
        mtA.start();
        mtB.start();
    }
}

结果:

MyThreadA execute start
MyThreadA execute end
MyThreadB print start
MyThreadB print end

总结

  1. 方法内的变量是线程安全的变量,实例变量为非线程安全的变量。
  2. synchronized锁支持重重入。
  3. synchronized可重入锁支持父子继承关系中
  4. 遇到异常,synchronized锁自动释放。
  5. synchronized修饰方法意味着对当前调用方法的对象加锁同步。
  6. synchronized(this)锁定当前的对象。
  7. synchronized(class)锁定指定的类。
  8. synchronized修饰静态方法意味着给静态方法所在的类上锁。

你可能感兴趣的:(synchronized锁)