精华内容
下载资源
问答
  • 目录什么是可重入锁(递归锁)sychronized版lock版为什么可重入锁能防止死锁什么是可重入锁(递归锁)可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁...

    目录

    什么是可重入锁(递归锁)

    sychronized版

    lock版

    为什么可重入锁能防止死锁

    什么是可重入锁(递归锁)

    可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁

    可重入锁最大的作用就是避免死锁

    bb47c54da0801cca063db5d8cf82ab70.png

    看这个小例子,method01和02都是加锁的方法,根据可重入锁的原理,线程只要获得了01的锁就能访问02,即使02也是个带锁的方法。

    c9934a9b81cd6fc08a1897c160d448cd.png

    sychronized版

    一个同步方法可进入另外一个同步方法

    // Synchronized

    public class Demo01 {

    public static void main(String[] args) {

    Phone phone = new Phone();

    new Thread(() -> {

    phone.sms();

    }, "A").start();

    new Thread(() -> {

    phone.sms();

    }, "B").start();

    }

    }

    class Phone {

    public synchronized void sms() {

    System.out.println(Thread.currentThread().getName() + "sms");

    call(); // 这里也有锁

    }

    public synchronized void call() {

    System.out.println(Thread.currentThread().getName() + "call");

    }

    }

    lock版

    sms方法是一个lock锁方法,这个方法调用了call方法,而call方法也是个lock锁方法。可重入锁指一个线程只要能进入sms方法,就也能进入到call方法中

    public class Demo02 {

    public static void main(String[] args) {

    Phone2 phone = new Phone2();

    new Thread(() -> {

    phone.sms();

    }, "A").start();

    new Thread(() -> {

    phone.sms();

    }, "B").start();

    }

    }

    class Phone2 {

    Lock lock = new ReentrantLock();

    public void sms() {

    lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否则就会死在里面

    try {

    System.out.println(Thread.currentThread().getName() + "sms");

    call(); // 这里也有锁

    } catch (Exception e) {

    e.printStackTrace();

    } finally {

    lock.unlock();

    }

    }

    public void call() {

    lock.lock();

    try {

    System.out.println(Thread.currentThread().getName() + "call");

    } catch (Exception e) {

    e.printStackTrace();

    } finally {

    lock.unlock();

    }

    }

    }

    为什么可重入锁能防止死锁

    这里举一个经典的例子。

    public class Widget {

    public synchronized void doSomething(){

    // do something

    }

    }

    public class LoggingWidget extends Widget {

    public synchronized void doSomething() {

    super.doSomething();

    }

    }

    现在假设synchronized不是可重入锁。

    Widget方法执行需要获得sync锁,默认情况下sync锁的是this,LoggingWidget是Widget的子类。当你执行LoggingWidget中的super.doSomething方法时它就会获得父类的this锁,但父类的this锁被子类抢走之后,没了锁就没法执行完毕。

    这样就出现死锁的现象,子类让父类执行完释放锁,而父类的锁在子类这里没法执行完毕。

    现在synchronized是可重入锁

    当进入到LoggingWidget方法时,即使LoggingWidget方法里面有一另一个锁也没关系,它也能获得Widget类中的内容,由于Widget类执行完毕了,LoggingWidget便可以继续执行。

    展开全文
  • 为什么可重入锁能防止死锁 什么是可重入锁(递归锁) 可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁 可重入锁最大的作用就是避免死锁 ...

    目录

    什么是可重入锁(递归锁)

    sychronized版

    lock版

    为什么可重入锁能防止死锁 


     

    什么是可重入锁(递归锁)

     

    可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁

    可重入锁最大的作用就是避免死锁

    看这个小例子,method01和02都是加锁的方法,根据可重入锁的原理,线程只要获得了01的锁就能访问02,即使02也是个带锁的方法。

     

     

    sychronized版

     

    一个同步方法可进入另外一个同步方法

    // Synchronized
    public class Demo01 {
        public static void main(String[] args) {
            Phone phone = new Phone();
            new Thread(() -> {
                phone.sms();
            }, "A").start();
            new Thread(() -> {
                phone.sms();
            }, "B").start();
        }
    }
    
    class Phone {
        public synchronized void sms() {
            System.out.println(Thread.currentThread().getName() + "sms");
            call(); // 这里也有锁
        }
    
        public synchronized void call() {
            System.out.println(Thread.currentThread().getName() + "call");
        }
    }

     

     

    lock版

     

    sms方法是一个lock锁方法,这个方法调用了call方法,而call方法也是个lock锁方法。可重入锁指一个线程只要能进入sms方法,就也能进入到call方法中

    
    public class Demo02 {
        public static void main(String[] args) {
            Phone2 phone = new Phone2();
            new Thread(() -> {
                phone.sms();
            }, "A").start();
            new Thread(() -> {
                phone.sms();
            }, "B").start();
        }
    }
    
    class Phone2 {
        Lock lock = new ReentrantLock();
    
        public void sms() {
            lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否则就会死在里面
            try {
                System.out.println(Thread.currentThread().getName() + "sms");
                call(); // 这里也有锁
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void call() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "call");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

     

     

    为什么可重入锁能防止死锁 

     

    这里举一个经典的例子。

    public class Widget {
        public synchronized void doSomething(){
            // do something
        }
    }
    public class LoggingWidget extends Widget {
        public synchronized void doSomething() {
            super.doSomething();
        }
    }

    现在假设synchronized不是可重入锁。

    Widget方法执行需要获得sync锁,默认情况下sync锁的是this,LoggingWidget是Widget的子类。当你执行LoggingWidget中的super.doSomething方法时它就会获得父类的this锁,但父类的this锁被子类抢走之后,没了锁就没法执行完毕。

    这样就出现死锁的现象,子类让父类执行完释放锁,而父类的锁在子类这里没法执行完毕。

     

    现在synchronized是可重入锁

    当进入到LoggingWidget方法时,即使LoggingWidget方法里面有一另一个锁也没关系,它也能获得Widget类中的内容,由于Widget类执行完毕了,LoggingWidget便可以继续执行。

    展开全文
  • 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。一、同步注意事项1.1、方法内的变量为线程安全  “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全...

      线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

    一、同步注意事项

    1.1、方法内的变量为线程安全
      “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题(这是方法内部的变量是私有的特性造成的,所得结果也就是“线程安全”的了,方法的变量存放在JVM里的虚拟机栈里)。

    2、实例变量非线程安全
      如果多个线程共同访问1个对象中的实例变量,则可能出现”非线程安全“问题。
      如果对象仅有1个实例变量,则有可能出现覆盖的情况。

    // 简单一个类
    public class HasSelfPrivateNum {
    
        //如果把HasSelfPrivateNum类里的synchronized关键字去除,则单例模式中的实例变量为非线程安全状
        //可能两个值 为一样
        public synchronized void add(String username) {
            try {
                int num;
                if (username.equals("a")) {
                    num = 100;
                    System.out.println("when username is a, then set num 100");
                    Thread.sleep(1000);
                } else {
                    num = 200;
                    System.out.println("when username is not a, then set num 200");
                }
    
                System.out.println(username + ", num=" + num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 两个线程
    public class MyThread1 extends Thread {
        private HasSelfPrivateNum hasSelfPrivateNum;
    
        public MyThread1(HasSelfPrivateNum hasSelfPrivateNum) {
            super();
            this.hasSelfPrivateNum = hasSelfPrivateNum;
        }
    
        public void run() {
            hasSelfPrivateNum.add("a");
        }
    }
    
    public class MyThread2 extends Thread {
        private HasSelfPrivateNum hasSelfPrivateNum;
    
        public MyThread2(HasSelfPrivateNum hasSelfPrivateNum) {
            super();
            this.hasSelfPrivateNum = hasSelfPrivateNum;
        }
    
        public void run() {
            hasSelfPrivateNum.add("b");
        }
    }
    
    //测试类
    public class Test1 {
        public static void main(String[] args) {
            // 如果多个线程共同访问1个对象中的实例变量,则可能出现”非线程安全“问题。
            // 如果对象仅有1个实例变量,则有可能出现覆盖的情况。
            HasSelfPrivateNum numRef = new HasSelfPrivateNum();//只创建一个对象
    
            Thread myThread1 = new MyThread1(numRef);
            myThread1.start();
    
            Thread myThread2 = new MyThread2(numRef);
            myThread2.start();
        }
    }

    when username is a, then set num 100
    a, num=100
    when username is not a, then set num 200
    b, num=200
      当然结果也有可能是先输出b,再输出a,线程有随机性
      如果把HasSelfPrivateNum类里的synchronized关键字去除,则单例模式中的实例变量为非线程安全状态,结果为
    when username is a, then set num 100
    when username is not a, then set num 200
    b, num=200
    a, num=100

    3、多个对象多个锁
      上面的HasSelfPrivateNum类,MyThread1,MyThread2不变,只修改main方法代码如下

    public class Test2 {
        public static void main(String[] args) {
            // 多个对象多个锁
            // 本示例由于创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的.
            HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
            HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
    
            Thread aThread1 = new MyThread1(numRef1);
            aThread1.start();
    
            Thread bThread2 = new MyThread2(numRef2);
            bThread2.start();
        }
    }

    when username is a, then set num 100
    when username is not a, then set num 200
    b, num=200
    a, num=100
      上面示例是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步的方式运行的。本示例由于创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的.
      从上面程序运行结果来看,虽然在HasSelfPrivateNum中使用了synchronized关键字,但打印的顺序却不是同步的,是交叉的。为什么是这样的结果呢?
      关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,所以在上面的示例中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
      但如果多个线程访间多个对象,则JVM会创建多个锁。上面的示例就是创建了2个HasSelfPrivateNumjava类的对象,所以就会产生出2个锁。
      同步的单词为synchronized,异步的单词为asynchronized。

      结论:调用用关键字synchronized声明的方法一定是排队运行的。另外需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要。

    4、脏读
      虽然在赋值时进行了同步,但在取值时有可能出现一些意想不到的意外,这种情况就是脏读(dirtyRead)。发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了

      1).脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的。也就是读取了其他事务还没有提交的数据
      2).不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了。当前事务已经读取的数据记录,被其他事务修改或删除。
      3).幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行。其他事务插入了新的数据,当前事务以相同的查询条件,在那个事务插入数据之前和之后查询数据,得到的数据记录的条数不一样

    // 简单的get,set方法实现
    public class PublicVar {
        public String username = "username";
        public String password = "password";
    
        public synchronized void setValue(String username, String password) {
            try {
                this.username = username;
                Thread.sleep(3000);
                this.password = password;
                System.out.println("setValue method thread name=" + Thread.currentThread().getName()
                        + ",  username=" +username + ",  password="+password);
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void getValue() {  //如果加synchronized,就不会出现脏读
            System.out.println("getValue method thread name=" + Thread.currentThread().getName()
                    + ",  username=" +username + ",  password="+password);
        }
    }
    
    // 创建一个线程
    public class MyThread3 extends Thread {
        private PublicVar publicVar;
    
        public MyThread1(PublicVar publicVar) {
            super();
            this.publicVar = publicVar;
        }
    
        public void run() {
            publicVar.setValue("username2", "password2");
        }
    }
    
    // 测试类
    public class Test3 {
        public static void main(String[] args) throws InterruptedException {
            // 这个测试类出现脏读是因为public void getValue()方法并不是同步的
            PublicVar publicVar = new PublicVar();
    
            Thread thread3 = new MyThread3(publicVar);
            thread3.start();
    
            Thread.sleep(2000);
            publicVar.getValue();
        }
    }

    getValue method thread name=main, username=username2, password=password
    setValue method thread name=Thread-0, username=username2, password=password2

      上面的结果在取值时出现脏读,username变成了username2了
      出现脏读是因为public void getValue()方法并不是同步的,所以可以在任意时候进行调用。解决办法当然就是加上同步synchronized关键字
      可见,方法setValue()和getValue()被依次执行。通过这个案例不仅要知道脏读是通过synchronized关键字解决的,还要知道如下内容:
      当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。
      当A线程调用anyObject对象加入synchronized关健字的X方法时,A线程就获得了X方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,而B线程如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。这时A线程已经执行了一个完整的任务,也就是说usernarne和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。
      脏读一定会出现操作实例变量的情况下,这就是不同线程“争抢”实例变量的结果。

    5、synchronized锁重入
      关键字synchronized拥有锁重入的功能。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
      “可重入锁”的概念是:自己可以再次获取自己的内部锁。比如有1个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
      可重入锁也支持在父子类继承的环境中。

    public class Service {
        public synchronized void service1() {
            System.out.println("service1");
            service2();
        }
    
        public synchronized void service2() {
            System.out.println("service2");
            service3();
        }
    
        public synchronized void service3() {
            System.out.println("service3");
        }
    }
    
    // 线程
    public class MyThread4 extends Thread {
        public void run() {
            Service service = new Service();
            service.service1();
        }
    }
    
    // 测试类
    public class Test4 {
        public static void main(String[] args) {
            Thread myThread4 = new MyThread4();
            myThread4.start();
        }
    }

    service1
    service2
    service3

    6、出现异常,锁自动释放
      当一个线程执行的代码出现异常时,其所持有的锁会自动释放。使用这个可以用来停止线程

    public class Service {
       public synchronized void testMethod() {
           if (Thread.currentThread().getName().equals("a")) {
               System.out.println("ThreadName=" + Thread.currentThread().getName()
                       +",  run beginTime=" + System.currentTimeMillis());
               int i = 1;
               while (i == 1) {
                   if (("" + Math.random()).substring(0, 8).equals("0.123456")) {
                       System.out.println("ThreadName=" + Thread.currentThread().getName()
                               +",  run beginTime=" + System.currentTimeMillis());
                       Integer.parseInt("a");
                   } else {
                       System.out.println("Thread B run Time=" + System.currentTimeMillis());
                   }
               }
           }
       }
    }
    public class MyThread1 extends Thread {
        private Service service;
    
        public MyThread1(Service service) {
            super();
            this.service = service;
        }
    
        public void run() {
            service.testMethod();
        }
    }
    public class MyThread2 extends Thread{
        private Service service;
    
        public MyThread2(Service service) {
            super();
            this.service = service;
        }
    
        public void run() {
            service.testMethod();
        }
    }
    //测试类
    public class Test {
        public static void main(String[] args) {
            try {
                Service service = new Service();
                MyThread1 aThread = new MyThread1(service);
                aThread.setName("a");
                aThread.start();
    
                Thread.sleep(1000);
                MyThread2 bThread2 = new MyThread2(service);
                bThread2.setName("b");
                bThread2.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    Thread B run Time=1462333804804
    Thread B run Time=1462333804804
    Thread B run Time=1462333804804
    ThreadName=a, run beginTime=1462333804804
    Exception in thread “a” java.lang.NumberFormatException: For input string: “a”
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)   
      线程a出现异常并释放锁,线程b进入方法正常打印,实验的结论就 出现异常的锁被自动释放。

    7、同步不具有继承性


    二、死锁

      Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中,“死锁”是必须避免的,因为这会造成线程的“假死”。

    产生死锁的四个必要条件
      1).互斥条件:一个资源每次只能被一个进程使用。
      2).请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
      3).不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
      4).循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    // 死锁线程
    public class DeathThread1 implements Runnable {
        public String username;
        public Object lock1 = new Object();
        public Object lock2 = new Object();
    
        public void setFlag(String username) {
            this.username = username;
        }
    
        public void run() {
            if (username.equals("a")) {
                synchronized (lock1) {
                    try {
                        System.out.println("username = " + username);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    synchronized (lock2) {
                        System.out.println("按lock1->lock2代码顺序执行了");
                    }
                }
            }
    
            if (username.equals("b")) {
                synchronized (lock2) {
                    try {
                        System.out.println("username = " + username);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    synchronized (lock1) {
                        System.out.println("按lock1->lock2代码顺序执行了");
                    }
                }
            }
        }
    }
    
    // 测试类
    public class DeathThreadTest_03 {
        public static void main(String[] args) throws InterruptedException {
            // ---------使用嵌套的synchronized代码结出现死锁--------------
            DeathThread1 deathThread1 = new DeathThread1();
            deathThread1.setFlag("a");
            Thread thread1 = new Thread(deathThread1);
            thread1.start();
            Thread.sleep(1000); //这个必须要有
    
            deathThread1.setFlag("b");
            Thread thread2 = new Thread(deathThread1);
            thread2.start();
        }
    }

    username = a
    username = b
    之后就不输出了,出现死锁

      死锁是程序设计的Bug.在设计程序时就要避免双方互相持有对方的锁的情况。需要说明的是,本实验使用synchronized嵌套的代码结构来实现死锁,其实不使用嵌套的synchronized代码结构也会出现死锁,与嵌套不嵌套无任何的关系,不要被代码结构所误导。


    三、同步块

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

    // 业务类
    public class Task {
        // 不在synchronized块中就是异步执行,在synchronized块中就是同步执行。
        public void doLongTimeTask() {
            for (int i=0; i<100; i++) {
                System.out.println("asynchronized threadName=" + Thread.currentThread().getName()
                        + ",  i=" + (i+1));
            }
    
            synchronized (this) {
                for (int i=0; i<100; i++) {
                    System.out.println("synchronized threadName=" + Thread.currentThread().getName()
                            + ",  i=" + (i+1));
                }
            }
        }
    
    }
    
    // 线程类
    public class MyThread5 extends Thread {
        private Task task;
    
        public MyThread5(Task task) {
            super();
            this.task = task;
        }
    
        public void run() {
            task.doLongTimeTask();
        }
    }
    
    // 测试类
    public class Test5 {
        public static void main(String[] args) {
            Task task = new Task();
            Thread thread1 = new MyThread5(task);
            thread1.start();
    
            Thread thread2 = new MyThread5(task);
            thread2.start();
        }
    }

    ……
    asynchronized threadName=Thread-0, i=78
    asynchronized threadName=Thread-1, i=100
    asynchronized threadName=Thread-0, i=79
    synchronized threadName=Thread-1, i=1
    asynchronized threadName=Thread-0, i=80
    synchronized threadName=Thread-1, i=2
    asynchronized threadName=Thread-0, i=81
    synchronized threadName=Thread-1, i=3
    ……
    synchronized threadName=Thread-1, i=98
    synchronized threadName=Thread-1, i=99
    synchronized threadName=Thread-1, i=100
    synchronized threadName=Thread-0, i=1
    synchronized threadName=Thread-0, i=2
    synchronized threadName=Thread-0, i=3
    ……
    可以看到结果里的非同步时交叉打印,同步是排除执行

      多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
      这说明synchronized同步方法或synchronized(this)同步代码块分别有两种作用。
      1)对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
      2)同一时间只有一个线程可以执行synchronized同步方法中的代码。

    2、将任意对象作为对象监视器
      在前面的学习中,使用synchronized(this)格式来同步代码块,其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。
      根据前面对:synchronized(this)同步代码块的作用总结可知,synchronized(非this对象)格式的作用只有1种:synchronized(非this对象x)同步代码块。
      1)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行:synchronized(非this对象x)同步代码块中的代码。
      2)当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
    示例:测试synchronized (非this对象x)是同一对象

    public class Service2 {
        private String username;
        private String password;
        //注释1,这里测试synchronized (非this对象x)是同一对象
        private String anyString = new String();
    
        // 使用“synchronized(非this对象x)同步代码块”格式进行同步操作时,对象监视器必须是同一个对象。
        // 如果不是同一个对象监视器,运行的结果就是异步调用了(如果两个方法的监视器不同也一样),就会交叉运行。
        public void setUsernamePwd(String username, String password) {
            //注释2,这里测试synchronized (非this对象x)不是同一对象,结果会交替执行
    //        String anyString = new String();
            try {
                synchronized (anyString) {
                    System.out.println("Thread name=" + Thread.currentThread().getName()
                            +" 在 "+System.currentTimeMillis() + " 进入同步块");
                    this.username = username;
                    Thread.sleep(2000);
                    this.password = password;
                    System.out.println("Thread name=" + Thread.currentThread().getName()
                            +" 在 "+System.currentTimeMillis() + " 离开同步块");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 两个线程类
    public class MyThread6 extends Thread{
        private Service2 service2;
    
        public MyThread6(Service2 service2) {
            super();
            this.service2 = service2;
        }
    
        public void run() {
            service2.setUsernamePwd("b", "bb");
        }
    }
    
    public class MyThread7 extends Thread {
        private Service2 service2;
    
        public MyThread7 (Service2 service2) {
            super();
            this.service2 = service2;
        }
    
        public void run() {
            service2.setUsernamePwd("a", "aa");
        }
    }
    
    public class Test6 {
        public static void main(String[] args) {
            // 如果不是同一个对象监视器,运行的结果就是异步调用了(如果两个方法的监视器不同也一样),就会交叉运行。
            Service2 service2 = new Service2();
            Thread a = new MyThread6(service2);
            a.setName("A");
            a.start();
    
            Thread b = new MyThread7(service2);
            b.setName("B");
            b.start();
        }
    }

    Thread name=A 在 1503808454604 进入同步块
    Thread name=A 在 1503808456605 离开同步块
    Thread name=B 在 1503808456605 进入同步块
    Thread name=B 在 1503808458609 离开同步块
      锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则(synchronized非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。

      如果将Service类里注释1下的代码注释,注释2下的代码打开,则结果如下
    Thread name=B 在 1503808749563 进入同步块
    Thread name=A 在 1503808749563 进入同步块
    Thread name=B 在 1503808751567 离开同步块
    Thread name=A 在 1503808751567 离开同步块
      可见,使用“synchronized(非this对象x)同步代码块”格式进行同步操作时,对象监视器必须是同一个对象。如果不是同一个对象监视器,运行的结果就是异步调用了(如果两个方法的监视器不同也一样),就会交叉运行。

    结论:
    “synchronized(非this对象x)”格式的写法是将x对象本身作为“对象监视器”可以得出以下3个结论:
      1)当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
      2)当其他线程执行x对象中synchronized同步方法时呈同步效果。
      3)当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果。
    但需要注意:如果其他线程调用不加synchronized关键字的方法时,还是异步调用。

    3.静态同步synchronized方法与synchronized(class)代码块
      synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。再者并不是同一个锁,证明如下

    // 一个是静态方法,一个是普通方法
    public class Service3 {
        public synchronized static void printA() {
            try {
                System.out.println("Thread name=" + Thread.currentThread().getName()
                        +" 在 "+System.currentTimeMillis() + " 进入同步块");
                Thread.sleep(2000);
                System.out.println("Thread name=" + Thread.currentThread().getName()
                        +" 在 "+System.currentTimeMillis() + " 离开同步块");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized void printB() { //如果这里也是static,则运行为是同步
            try {
                System.out.println("Thread name=" + Thread.currentThread().getName()
                        +" 在 "+System.currentTimeMillis() + " 进入同步块");
                Thread.sleep(2000);
                System.out.println("Thread name=" + Thread.currentThread().getName()
                        +" 在 "+System.currentTimeMillis() + " 离开同步块");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 两个线程
    public class MyThread8 extends Thread {
        private Service3 service3;
    
        public MyThread8(Service3 service3) {
            super();
            this.service3 = service3;
        }
    
        public void run() {
            service3.printA();
        }
    }
    
    public class MyThread9 extends Thread{
        private Service3 service3;
    
        public MyThread9 (Service3 service3) {
            super();
            this.service3 = service3;
        }
    
        public void run() {
            service3.printB();
        }
    }
    
    // 测评类
    public class Test8 {
        public static void main(String[] args) {
            /*
              - synchronized关键字加到static静态方法上是给Class类上锁,
              而synchronized关键字加到非static静态方法上是给对象上锁。
              - 两者并不是同一个锁
              - 异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁可以对类的所有对象实例起作用。
             */
            // 两个方法都加static就能同步,或者多个线程启动多个实例也能同步
            Service3 service3 = new Service3();
            Thread thread8 = new MyThread8(service3);
            thread8.setName("A");
            thread8.start();
    
            Thread thread9 = new MyThread9(service3);
            thread9.setName("B");
            thread9.start();
        }
    }

    Thread name=A 在 1503811254382 进入同步块
    Thread name=B 在 1503811254382 进入同步块
    Thread name=A 在 1503811256386 离开同步块
    Thread name=B 在 1503811256386 离开同步块
      异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁可以对类的所有对象实例起作用。
      如果在Service类里的printB方法前加上synchronized,并将测试类修改为:

    //如果在Service类里的printB方法前加上synchronized
    public class Run {
        public static void main(String[] args) {
            Service3 service1 = new Service3();
            Service3 service2 = new Service3();
    
            MyThread1 a = new MyThread8(service1);
            a.setName("A");
            a.start();
    
            MyThread2 b = new MyThread9(service2);
            b.setName("B");
            b.start();
        }
    }

    Thread name=B 在 1462348587331 进入同步块
    Thread name=B 在 1462348589332 离开同步块
    Thread name=A 在 1462348589332 进入同步块
    Thread name=A 在 1462348591332 离开同步块
      虽然是不同对象,但静态的同步方法还是同步运行的。
      同步synchronized(class)代码块的作用其实和synchronized static方法的作用一样。将上面的Service类修改为如下结果也是上面一样

        public  static void printA() {
            synchronized(Service.class) {
                //对应的代码
            }
        }
    
        public  static void printB() {
            synchronized(Service.class) {
                //对应的代码
            }
        }

    4.数据类型String的常量池特性
      在JVM中具有String常量池缓存的功能,如下

        @Test
        public  void test() {
            String a = "a";
            String b = "a";
            System.out.println(a==b); //true
        }

      将synchronized(string)同步块与String联合使用时,要注意常量池带来的一些例外

    public class Service5 {
        public static void print(String str) {
            try {
                synchronized (str) {
                    while (true) {
                        System.out.println("currentThread:" +Thread.currentThread().getName());
                        Thread.sleep(1000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 两个线程
    public class MyThread13 extends Thread {
        private Service5 service5;
    
        public MyThread13(Service5 service5) {
            super();
            this.service5 = service5;
        }
    
        public void run() {
            service5.print("AA");
        }
    }
    
    public class MyThread14 extends Thread {
        private Service5 service5;
    
        public MyThread14(Service5 service5) {
            super();
            this.service5 = service5;
        }
    
        public void run() {
            service5.print("AA");  // //一定要跟上面相同
        }
    }
    
    // 测试类
    public class Test10 {
        public static void main(String[] args) {
            Service5 service5 = new Service5();
    
            Thread a = new MyThread13(service5);
            a.setName("A");
            a.start();
    
            Thread b = new MyThread14(service5);
            b.setName("B");
            b.start();
        }
    }

    输出结果:全为A,或者全为B
      出现这样的原因是因为String的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此在大多数情况下,同步synchronized代码块都不能使用String作为锁对象,而改用其他,比如new Object()实例化一个Object对象,但它并不放入缓存中。

    6、同步synchronized方法无限等待与解决
      同步方法容易造成死循环,而同步块可以解决死循环

    //同步方法
    public class Service {
       public synchronized void methodA() {
           System.out.println("methodA begin");
           boolean flag = true;
           while (flag) {//故意死循环
           }
           System.out.println("methodB end");
       }
    
        public synchronized void methodB() {
            System.out.println("methodB begin");
            System.out.println("methodB end");
        }
    }

      自定义线程代码,就是将上面的MyThread14和MyThread15里的run方法分别修改为service.methodA();和service.methodB(); 测试类Run不变,结果为:
    methodA begin
      线程B永远不会运行,死锁。

      这里就可以使用同步块来解决这样的问题了。更改Service.java文件后如下:

    //同步块
    public class Service {
        Object object1 = new Object();
        public void methodA() {
            synchronized (object1) {
                System.out.println("methodA begin");
                boolean flag = true;
                while (flag) {//故意死循环
                }
                System.out.println("methodB end");
            }
        }
    
        Object object2 = new Object();
        public void methodB() {
            synchronized (object2) {
                System.out.println("methodB begin");
                System.out.println("methodB end");
            }
        }
    }

    methodA begin
    methodB begin
    methodB end
      需要注意的是:虽然线程B执行了,但线程A还是死循环。

    展开全文
  • 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。1、方法内的变量为线程安全  “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题(这是方法内部...

        线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

    1、方法内的变量为线程安全
      “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题(这是方法内部的变量是私有的特性造成的,所得结果也就是“线程安全”的了。

    2、实例变量非线程安全
        如果多个线程共同访问1个对象中的实例变量,则可能出现”非线程安全“问题。
        如果对象仅有1个实例变量,则有可能出现覆盖的情况。

    1. public class HasSelfPrivateNum {  
    2.     private int num = 0;  
    3.   
    4.     public synchronized void addI(String username) {  
    5.         try {  
    6.             if (username.equals(“a”)) {  
    7.                 num = 100;  
    8.                 System.out.println(”a set over!”);  
    9.                 Thread.sleep(2000);  
    10.             } else {  
    11.                 num = 200;  
    12.                 System.out.println(”b set over!”);  
    13.             }  
    14.   
    15.             System.out.println(username + ” num=” + num);  
    16.         } catch (InterruptedException e) {  
    17.             e.printStackTrace();  
    18.         }  
    19.   
    20.     }  
    21. }  
    save_snippets.png
    public class HasSelfPrivateNum {
        private int num = 0;
    
        public synchronized void addI(String username) {
            try {
                if (username.equals("a")) {
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                } else {
                    num = 200;
                    System.out.println("b set over!");
                }
    
                System.out.println(username + " num=" + num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    1. public class MyThread1 extends Thread {  
    2.     private HasSelfPrivateNum numRef;  
    3.   
    4.     public MyThread1(HasSelfPrivateNum numRef) {  
    5.         super();  
    6.         this.numRef = numRef;  
    7.     }  
    8.   
    9.     public void run() {  
    10.         numRef.addI(”a”);  
    11.     }  
    12. }  
    save_snippets.png
    public class MyThread1 extends Thread {
        private HasSelfPrivateNum numRef;
    
        public MyThread1(HasSelfPrivateNum numRef) {
            super();
            this.numRef = numRef;
        }
    
        public void run() {
            numRef.addI("a");
        }
    }
    
    1. public class MyThread2 extends Thread{  
    2.     private HasSelfPrivateNum numRef;  
    3.   
    4.     public MyThread2 (HasSelfPrivateNum numRef) {  
    5.         super();  
    6.         this.numRef = numRef;  
    7.     }  
    8.   
    9.     public void run() {  
    10.         numRef.addI(”b”);  
    11.     }  
    12. }  
    save_snippets.png
    public class MyThread2 extends Thread{
        private HasSelfPrivateNum numRef;
    
        public MyThread2 (HasSelfPrivateNum numRef) {
            super();
            this.numRef = numRef;
        }
    
        public void run() {
            numRef.addI("b");
        }
    }
    
    1. //内部类  
    2. class Test {  
    3.     public static void main(String[] args) {  
    4.         HasSelfPrivateNum numRef = new HasSelfPrivateNum();//只创建一个对象  
    5.   
    6.         MyThread1 myThread1 = new MyThread1(numRef);  
    7.         myThread1.start();  
    8.   
    9.         MyThread2 myThread2 = new MyThread2(numRef);  
    10.         myThread2.start();  
    11.     }  
    12. }  
    save_snippets.png
    //内部类
    class Test {
        public static void main(String[] args) {
            HasSelfPrivateNum numRef = new HasSelfPrivateNum();//只创建一个对象
    
            MyThread1 myThread1 = new MyThread1(numRef);
            myThread1.start();
    
            MyThread2 myThread2 = new MyThread2(numRef);
            myThread2.start();
        }
    }
    b set over!
    b num=200
    a set over!
    a num=100
    当然结果也有可能是先输出b,再输出a,线程有随机性
    如果把HasSelfPrivateNum类里的synchronized关键字去除,则单例模式中的实例变量为非线程安全状态,结果为
    a set over!
    b set over!
    b num=200
    a num=200

    3、多个对象多个锁
       上面的HasSelfPrivateNum类,MyThread1,MyThread2不变,只修改main方法代码如下

    1. //内部类,这个方法里创建了两个HasSelfPrivateNum对象  
    2. class Test {  
    3.     public static void main(String[] args) {  
    4.         HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();  
    5.         HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();  
    6.   
    7.         MyThread1 aThread1 = new MyThread1(numRef1);  
    8.         aThread1.start();  
    9.   
    10.         MyThread2 bThread2 = new MyThread2(numRef2);  
    11.         bThread2.start();  
    12.     }  
    13. }  
    save_snippets.png
    //内部类,这个方法里创建了两个HasSelfPrivateNum对象
    class Test {
        public static void main(String[] args) {
            HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
            HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
    
            MyThread1 aThread1 = new MyThread1(numRef1);
            aThread1.start();
    
            MyThread2 bThread2 = new MyThread2(numRef2);
            bThread2.start();
        }
    }
    a set over!
    b set over!
    b num=200
    a num=100
        上面示例是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步的方式运行的。本示例由于创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的.   
        从上面程序运行结果来看,虽然在Has SelfPrivateNumJava中使用了synchronized关键字,但打印的顺序却不是同步的,是交叉的。为什么是这样的结果呢?
        关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,所以在上面的示例中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
        但如果多个线程访间多个对象,则JVM会创建多个锁。上面的示例就是创建了2个HasSelfPrivateNumjava类的对象,所以就会产生出2个锁。
        同步的单词为synchronized,异步的单词为asynchronized。

    结论:调用用关键字synchronized声明的方法一定是排队运行的。另外需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要。

    —————————————————————————————————————————–

    4、脏读

        虽然在赋值时进行了同步,但在取值时有可能出现一些意想不到的意外,这种情况就是脏读(dirtyRead)。发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。

    1. public class PublicVar {  
    2.     public String username = “username”;  
    3.     public String password = “password”;  
    4.   
    5.     public synchronized void setValue(String username, String password) {  
    6.         try {  
    7.             this.username = username;  
    8.             Thread.sleep(3000);  
    9.             this.password = password;  
    10.             System.out.println(”setValue method thread name=” + Thread.currentThread().getName()  
    11.                     + ”,  username=” +username + “,  password=”+password);  
    12.         } catch (InterruptedException e) {  
    13.             e.printStackTrace();  
    14.         }  
    15.     }  
    16.   
    17.     public void getValue() {  //如果加synchronized,就不会出现脏读  
    18.         System.out.println(”getValue method thread name=” + Thread.currentThread().getName()  
    19.                 + ”,  username=” +username + “,  password=”+password);  
    20.     }  
    21. }  
    save_snippets.png
    public class PublicVar {
        public String username = "username";
        public String password = "password";
    
        public synchronized void setValue(String username, String password) {
            try {
                this.username = username;
                Thread.sleep(3000);
                this.password = password;
                System.out.println("setValue method thread name=" + Thread.currentThread().getName()
                        + ",  username=" +username + ",  password="+password);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void getValue() {  //如果加synchronized,就不会出现脏读
            System.out.println("getValue method thread name=" + Thread.currentThread().getName()
                    + ",  username=" +username + ",  password="+password);
        }
    }
    
    1. public class MyThread1 extends Thread {  
    2.     private PublicVar publicVar;  
    3.   
    4.     public MyThread1(PublicVar publicVar) {  
    5.         super();  
    6.         this.publicVar = publicVar;  
    7.     }  
    8.   
    9.     public void run() {  
    10.         publicVar.setValue(”username2”“password2”);  
    11.     }  
    12. }  
    save_snippets.png
    public class MyThread1 extends Thread {
        private PublicVar publicVar;
    
        public MyThread1(PublicVar publicVar) {
            super();
            this.publicVar = publicVar;
        }
    
        public void run() {
            publicVar.setValue("username2", "password2");
        }
    }
    1. class Test {  
    2.     public static void main(String[] args) throws InterruptedException {  
    3.         PublicVar publicVar = new PublicVar();  
    4.   
    5.         MyThread1 myThread1 = new MyThread1(publicVar);  
    6.         myThread1.start();  
    7.   
    8.         Thread.sleep(2000);  
    9.         publicVar.getValue();  
    10.     }  
    11. }  
    save_snippets.png
    class Test {
        public static void main(String[] args) throws InterruptedException {
            PublicVar publicVar = new PublicVar();
    
            MyThread1 myThread1 = new MyThread1(publicVar);
            myThread1.start();
    
            Thread.sleep(2000);
            publicVar.getValue();
        }
    }
    getValue method thread name=main,  username=username2,  password=password
    setValue method thread name=Thread-0,  username=username2,  password=password2
    上面的结果在取值时出现脏读,username变成了username2了
        出现脏读是因为public void getValue()方法并不是同步的,所以可以在任意时候进行调用。解决办法当然就是加上同步synchronized关键字

      可见,方法setValue()和getValue()被依次执行。通过这个案例不仅要知道脏读是通过synchronized关键字解决的,还要知道如下内容:
        当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了时象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。
        当A线程调用anyObject对象加入synchronized关健字的X方法时,A线程就获得了X方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,而B线程如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。这时A线程已经执行了一个完整的任务,也就是说usernarne和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。

      脏读一定会出现操作实例变量的情况下,这就是不同线程“争抢”实例变量的结果。
    —————————————————————————————————————–

    5、synchronized锁重入
        关键字synchronized拥有锁重入的功能。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
        “可重入锁”的概念是:自己可以再次获取自己的内部锁。比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
        可重入锁也支持在父子类继承的环境中。

    1. public class Service {  
    2.     public synchronized void service1() {  
    3.         System.out.println(”service1”);  
    4.         service2();  
    5.     }  
    6.   
    7.     public synchronized void service2() {  
    8.         System.out.println(”service2”);  
    9.         service3();  
    10.     }  
    11.   
    12.     public synchronized void service3() {  
    13.         System.out.println(”service3”);  
    14.     }  
    15. }  
    save_snippets.png
    public class Service {
        public synchronized void service1() {
            System.out.println("service1");
            service2();
        }
    
        public synchronized void service2() {
            System.out.println("service2");
            service3();
        }
    
        public synchronized void service3() {
            System.out.println("service3");
        }
    }
    
    1. public class MyThread extends Thread {  
    2.     public void run() {  
    3.         Service service = new Service();  
    4.         service.service1();  
    5.     }  
    6. }  
    save_snippets.png
    public class MyThread extends Thread {
        public void run() {
            Service service = new Service();
            service.service1();
        }
    }
    1. class Test {  
    2.     public static void main(String[] args) throws InterruptedException {  
    3.         MyThread myThread = new MyThread();  
    4.         myThread.start();  
    5.     }  
    6. }  
    save_snippets.png
    class Test {
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
        }
    }
    service1
    service2
    service3

    —————————————————————————————————-
    6、出现异常,锁自动释放
        当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

    1. public class Service {  
    2.    public synchronized void testMethod() {  
    3.        if (Thread.currentThread().getName().equals(“a”)) {  
    4.            System.out.println(”ThreadName=” + Thread.currentThread().getName()  
    5.                    +”,  run beginTime=” + System.currentTimeMillis());  
    6.            int i = 1;  
    7.            while (i == 1) {  
    8.                if ((“” + Math.random()).substring(08).equals(“0.123456”)) {  
    9.                    System.out.println(”ThreadName=” + Thread.currentThread().getName()  
    10.                            +”,  run beginTime=” + System.currentTimeMillis());  
    11.                    Integer.parseInt(”a”);  
    12.                } else {  
    13.                    System.out.println(”Thread B run Time=” + System.currentTimeMillis());  
    14.                }  
    15.            }  
    16.        }  
    17.    }  
    18. }  
    save_snippets.png
    public class Service {
       public synchronized void testMethod() {
           if (Thread.currentThread().getName().equals("a")) {
               System.out.println("ThreadName=" + Thread.currentThread().getName()
                       +",  run beginTime=" + System.currentTimeMillis());
               int i = 1;
               while (i == 1) {
                   if (("" + Math.random()).substring(0, 8).equals("0.123456")) {
                       System.out.println("ThreadName=" + Thread.currentThread().getName()
                               +",  run beginTime=" + System.currentTimeMillis());
                       Integer.parseInt("a");
                   } else {
                       System.out.println("Thread B run Time=" + System.currentTimeMillis());
                   }
               }
           }
       }
    }
    
    1. public class MyThread1 extends Thread {  
    2.     private Service service;  
    3.   
    4.     public MyThread1(Service service) {  
    5.         super();  
    6.         this.service = service;  
    7.     }  
    8.   
    9.     public void run() {  
    10.         service.testMethod();  
    11.     }  
    12. }  
    save_snippets.png
    public class MyThread1 extends Thread {
        private Service service;
    
        public MyThread1(Service service) {
            super();
            this.service = service;
        }
    
        public void run() {
            service.testMethod();
        }
    }
    
    1. public class MyThread2 extends Thread{  
    2.     private Service service;  
    3.   
    4.     public MyThread2(Service service) {  
    5.         super();  
    6.         this.service = service;  
    7.     }  
    8.   
    9.     public void run() {  
    10.         service.testMethod();  
    11.     }  
    12. }  
    save_snippets.png
    public class MyThread2 extends Thread{
        private Service service;
    
        public MyThread2(Service service) {
            super();
            this.service = service;
        }
    
        public void run() {
            service.testMethod();
        }
    }
    1. //内部类  
    2. class Test {  
    3.     public static void main(String[] args) {  
    4.         try {  
    5.             Service service = new Service();  
    6.             MyThread1 aThread = new MyThread1(service);  
    7.             aThread.setName(”a”);  
    8.             aThread.start();  
    9.   
    10.             Thread.sleep(1000);  
    11.             MyThread2 bThread2 = new MyThread2(service);  
    12.             bThread2.setName(”b”);  
    13.             bThread2.start();  
    14.         } catch (InterruptedException e) {  
    15.             e.printStackTrace();  
    16.         }  
    17.     }  
    18. }  
    save_snippets.png
    //内部类
    class Test {
        public static void main(String[] args) {
            try {
                Service service = new Service();
                MyThread1 aThread = new MyThread1(service);
                aThread.setName("a");
                aThread.start();
    
                Thread.sleep(1000);
                MyThread2 bThread2 = new MyThread2(service);
                bThread2.setName("b");
                bThread2.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    Thread B run Time=1462333804804
    Thread B run Time=1462333804804
    Thread B run Time=1462333804804
    ThreadName=a,  run beginTime=1462333804804
    Exception in thread “a” java.lang.NumberFormatException: For input string: “a”
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        线程a出现异常并释放锁,线程b进入方法正常打印,实验的结论就 出现异常的锁被自动释放。

    7、同步不具有继承性

    展开全文
  • 重入

    2021-04-28 20:46:50
    重入锁的设计目的是为了防止死锁。例如在多个方法互相调用的场景,需要保证可重入,不然很容易死锁。 java中的synchronized和ReentrantLock都是可重入锁,只限同一个线程,子线程也不可以。 public static ...
  • 我想防止函数重入,在C中 fun(){ static int flag=0; if(flag)return; flag=1; ... flag=0; } 这样就好,在java中想完成类似的功能,可以怎么做呢。 我使用了static好像报错了。 如 public static void fun(){ ...
  • java并发 目录 java并发关键字 Volatile Synchronized ...java内存模型 ...2.讲一下Java内存模型吧?...java线程及通信 ...3.wait方法能不能被重写,wait能不能被中断;...2、重入锁的概念,重入锁为什么可以防止...
  • 一种可重入的互斥锁,经由Java5引入,支持一个线程对资源的重复加锁。它和synchronized语句和方法访问的隐式监视器锁,有相同的基本行为和语义,但是功能更强大。 一、Lock接口 锁对象,在java中锁是控制多个线程...
  • java 语言中,Java 程序的基本单位是类,也就是说:一个 Java 程序是由多个类组成 的。定义一个类与定义一个数据类型是有区别的。在程序设计语言中,把定义数据类型的能 力作为一种很重要的能力来对待。在面向...
  • java 面试题 总结

    2009-09-16 08:45:34
    java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,...
  • 重入锁:这种锁可以反复进入,可以响应中断(lockInterruptibly()方法),可以申请等待时限(tryLock(),tryLock(long timeout, TimeUnit unit)),可以产生公平锁防止饥饿(new ReentrantLock(true))。案例代码: import ...
  • java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 9、说出Servlet的生命周期,并说出Servlet和CGI的区别。  Servlet被服务器实例化后,容器运行其init方法...
  • 常用多线程方法

    2019-09-30 19:01:52
    重入锁 ​ ReentrantLock类、synchronized关键字,属于悲观锁。 ​ 可重入锁,即递归锁。指在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获得该锁。 ​ 作用:防止在同一线程中多次获取锁而导致死锁...
  • 具备可重入的特性。 具备锁失效机制,防止死锁。 具备阻塞锁特性,即没有获取到锁将会继续等待获取锁。 具备非阻塞锁特性,即没有获取到锁将会直接返回获取锁失败。 spring mvc 组件 在学习9个组件之前,我们...
  • 分布式锁

    2018-11-14 13:23:45
    java中,多线程环境下,需要控制对资源的并发访问,通常会使用synchronized关键字或者ReentrantLock可重入锁,不过在分布式环境下需要新的方法来加锁,才能满足需要。 为什么需要分布式锁? 分布式锁要解决的问题...
  • 分布式锁应该具备的条件   在分析分布式锁的三种实现... 具备可重入特性; 具备锁失效机制,防止死锁; 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。 作者:Java架构师养成记 链接:https://jueji...
  • Java中,我们一般通过集成Thread类和实现Runnnable接口,调用线程的start()方法实现线程的启动。但如果并发的数量很多,而且每个线程都是执行很短的时间便结束了,那样频繁的创建线程和销毁进程会大大的降低系统...
  • 新版Android开发教程.rar

    千次下载 热门讨论 2010-12-14 15:49:11
    � Google 提供了一套 Java 核心包 (J2SE 5,J2SE 6) 的有限子集,尚不承诺遵守 Java 任何 Java 规范 , 可能会造 成J ava 阵营的进一步分裂。 � 现有应用完善度不太够,需要的开发工作量较大。--------------------...
  • java.util.List result = new java.util.ArrayList(); result.add(opdata1); result.add(opdata2); return result; } } } 如何使用Operator //(1)addOperator ExpressRunner runner = new ...
  • 网络异常的时候自动关闭VPN(防止VPN有效性过期),并且自动断开Wi-Fi,再次重新连接(防止IP有效期超时);重启sb、在sb 进行搭建操作界面等。 2017年10月-至今 app逆向开发(非越狱App集成、tweak) 项目描述...
  • 目前,学校工作繁杂、资料多,管理信息系统已进入高校,但还未普及,而对于学生成绩管理来说,还没有一套完整的、统一的系统。因此,开发一套适和大众的、兼容性好的系统是很有必要的。根据开发要求,它主要应用于...
  • 你还在为 TCP 传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了 敖丙说了这么多次 I/O,可你知道其中的原理么? 敖丙听说你 ping 用的很 6 ?给我图解一下 ping 的工作原理! 硬核!30 张图解 HTTP 常见...
  • 解决方法:试试装豪杰超级解霸,如果装后还会,到官方网站下载相应版本的补丁试试。还不行,只好换就用别的播放器试试了。 例六:双击一个游戏的快捷方式,“0x77f5cd0”指令引用“0xffffffff”内 存,该内存不...
  • 第06节、重入锁 第07节、读写锁 第08节、CAS无锁机制 第09节、自旋锁 资料+源码.rar 0006-蚂蚁课堂(每特学院)-2期-数据交换格式&反射机制&SpringIOC;原理分析 第01节、什么是数据交换格式 第02节、什么是json 第03...
  • asp.net知识库

    2015-06-18 08:45:45
    动态调用对象的属性和方法——性能和灵活性兼备的方法 消除由try/catch语句带来的warning 微软的应试题完整版(附答案) 一个时间转换的问题,顺便谈谈搜索技巧 .net中的正则表达式使用高级技巧 (一) C#静态成员和...
  • 3.方法Java代码设置 final RefreshLayout refreshLayout = (RefreshLayout) findViewById(R.id.refreshLayout); //设置 Header 为 贝塞尔雷达 样式 refreshLayout.setRefreshHeader(new BezierRadarHeader(this)....
  • 1.3.2跨域交互即缓存处理方法 7 1.3.3页面自适应机制,即设备自适应与浏览器自适应机制 8 1.3.4 “认我测”质检服务平台的设计和实现 8 1.4 本文的结构安排 8 第二章 多窗口类浏览器设计 11 2.1 多窗口类浏览器需求...
  • 在新的编程思想中,指针基本上被禁止使用(JAVA中就是这样),至少也是被限制使用。而在我们交换机的程序中大量使用指针,并且有增无减。 2、防止指针/数组操作越界 【案例1.2.1】 在香港项目测试中,发现ISDN话机...

空空如也

空空如也

1 2
收藏数 29
精华内容 11
关键字:

java防止重入方法

java 订阅