• Java Synchronized


    1、synchronized 同步方法:

      是对当前对象加锁。

    package com.test;
    
    public class TestObject {
        
        synchronized public void methodA() {
            try {
                System.out.println("begin methodA threadName=" + Thread.currentThread().getName() + " beigin time = " + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("end methodA endTime=" + System.currentTimeMillis());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public void methodB() {
            try {
                System.out.println("begin methodB threadName=" + Thread.currentThread().getName() + " beigin time = " + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("end methodB endTime=" + System.currentTimeMillis());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    package com.test;
    
    public class Run {
        
        public static void main(String[] args) {
            TestObject object = new TestObject();
            
            Thread a = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    object.methodA();
                }
            });
            
            Thread b = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    object.methodB();
                }
            });
            
            a.start();
            b.start();
        }
    }
    运行结果:
    begin methodA threadName=Thread-0 beigin time = 1527756573018
    begin methodB threadName=Thread-1 beigin time = 1527756573018
    end methodB endTime=1527756574018
    end methodA endTime=1527756574018

    通过上面的代码可以得知,虽然线程A先持有了object对象的锁,但是线程B完全可以异步调用非synchronized类型的方法。

    如果将TestObject.java 中的methodB()方法前加上synchronized关键字。

    #methodB()前加synchronized关键字运行结果:
    begin methodA threadName=Thread-0 beigin time = 1527756647320
    end methodA endTime=1527756648321
    begin methodB threadName=Thread-1 beigin time = 1527756648321
    end methodB endTime=1527756649321

    结论:

    1. A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中非synchronized类型的方法。
    2. A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。

    总结:

      关键字synchronized 拥有锁重入的功能,也就是在使用synchronized时,但一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。

      同步不可以继承。

    2、synchronized同步语句块:

      是对某一个对象进行加锁。synchronized(this) 锁定的也是当前对象。

      用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长的时间。在这样的情况下可以使用synchronized同步语句块来解决。

    package com.test;
    
    
    public class Task {
        
        public void methodA() {
            try {
                synchronized (this) {
                    System.out.println("A begin time = " + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("A end time = " + System.currentTimeMillis());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public void methodB() {
            try {
                synchronized (this) {
                    System.out.println("B begin time = " + System.currentTimeMillis());
                    System.out.println("B end time = " + System.currentTimeMillis());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    package com.test;
    
    public class Run {
        
        public static void main(String[] args) {
            Task task = new Task();
            
            Thread a = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    task.methodA();
                }
            });
            
            Thread b = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    task.methodB();
                }
            });
            
            a.start();
            b.start();
        }
    }
    运行结果:
    A begin time = 1527757467600
    A end time = 1527757469601
    B begin time = 1527757469601
    B end time = 1527757469601

    在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的 对象监视器 是一个。

     将任意对象作为对象监视器:

      多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的阻塞的。

    package com.test;
    
    public class Service {
        private String anyString = new String();
        
        public void methodA() {
            try {
                synchronized (anyString) {
                    System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入同步块");
                    Thread.sleep(3000);
                    System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开同步块");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    package com.test;
    
    public class Run {
        
        public static void main(String[] args) {
            Service service = new Service();
            
            Thread a = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    service.methodA();
                }
            });
            
            Thread b = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    service.methodA();
                }
            });
            
            a.start();
            b.start();
        }
    }
    运行结果:
    线程名为:Thread-1在1527758321295进入同步块
    线程名为:Thread-1在1527758324295离开同步块
    线程名为:Thread-0在1527758324295进入同步块
    线程名为:Thread-0在1527758327295离开同步块

    使用 synchronized(非this对象x)同步代码块进行同步操作时,对象监视器必须是同一个对象。如果不是同一个对象,运行的结果就是异步了。

    把Service.java修改为如下:

    package com.test;
    
    public class Service {
        private String anyString = new String();
        
        public void methodA() {
            try {
                synchronized (anyString) {
                    System.out.println("A begin");
                    Thread.sleep(3000);
                    System.out.println("A end");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        synchronized public void methodB() {
            System.out.println("B begin");
            System.out.println("B end");
        }
    }
    运行结果:
    B begin
    A begin
    B end
    A end

    由于对象的监视器不同,所以运行结果就是异步的。

    3、静态同步synchronized方法与synchronized(class)代码块:

    关键字synchronized还可以应用在static静态方法上,如果这样写,那就是对当前的*.java文件对应的class类进行加锁。

    package com.test;
    
    public class Service {
        synchronized public static void methodA() {
            
            try {
                System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入同步块");
                Thread.sleep(3000);
                System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开同步块");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        synchronized public static void methodB() {
            
            try {
                System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入同步块");
                Thread.sleep(3000);
                System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开同步块");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    运行结果:
    线程名为:Thread-1在1527758944286进入同步块
    线程名为:Thread-1在1527758947287离开同步块
    线程名为:Thread-0在1527758947287进入同步块
    线程名为:Thread-0在1527758950287离开同步块

    synchronized关键字加到static静态方法上是给Class上锁,而synchronized关键字加到非static静态方法上是给对象上锁。所以同一个类下使用两种加锁方式的方法是可以进行异步调用的。

  • 相关阅读:
    Tarjan算法与割点割边
    kmp匹配
    最小表示法
    字典树
    哈希
    网络流入门浅谈
    关于两道搜索的题目
    2020 4.2校内测题解
    LIS最长上升子序列讲解&&洛谷P1439 【模板】最长公共子序列 题解
    浅谈拉格朗日插值公式
  • 原文地址:https://www.cnblogs.com/gouge/p/9117628.html
Copyright © 2020-2023  润新知