美文网首页
synchronized锁

synchronized锁

作者: sunpy | 来源:发表于2018-10-31 23:04 被阅读7次

    为什么使用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锁

          本文链接:https://www.haomeiwen.com/subject/gocntqtx.html