第二章、(1)synchronized 锁重入、子类可调用父类同步方法、异常自动释放锁、同步方法不可继承

摘要:

一、当线程A调用对象Object 的synchronized 方法 X 时,A线程获得了 X 的方法锁,更准确的说是 对象Object锁。所以其他线程想要再执行 X 方法必须等线程A执行完X方法。但是其他线程可以调用对象Object中的其他 非synchronized 同步方法。(从而形成脏读)

二、synchronized 锁重入:当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。也就是说,在一个synchronized方法/块 的内部调用本类的其他synchronized 方法/块时,是永远可以得到锁的。

三、当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。

四、出现异常,锁自动释放

五、同步方法不具备继承性,即父类方法使用 synchronized 声明,但子类 override 重写方法没有 synchronized 声明,那么子类该方法是非线程安全的。

 

5. 脏读:发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。

public class PublicVar {
    public String username = "A";
    public String password = "AA";
    public synchronized void setValue(String username, String password) {
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void getValue() {
        System.out.println("getValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
    }
}
public class ThreadA extends Thread {
    private PublicVar publicVar;
    public ThreadA(PublicVar publicVar) {
        super();
        this.publicVar = publicVar;
    }
    @Override
    public void run() {
        super.run();
        publicVar.setValue("B", "BB");
    }
}
public class Test {
    public static void main(String[] args) {
        try {
            PublicVar publicVarRef = new PublicVar();
            ThreadA threadA = new ThreadA(publicVarRef);
            threadA.start();
            Thread.sleep(200); //打印结果受此值大小影响
            System.out.println("main 将要执行 getValue() 方法");
            publicVarRef.getValue();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /*
    运行结果:
    main 将要执行 getValue() 方法
    getValue method threadname = main username = B password = AA
    setValue method threadname = Thread-0 username = B password = BB
     */
}

解释一下运行结果:线程A调用 publicVar对象的synchronized 的 setValue() 方法,执行完 username 的赋值为 B之后,执行 sleep() 方法休眠5s。这时 main线程执行非 synchronized getValue() 方法,此时 username 已经被线程A赋值为 B了。所以有执行结果:

getValue method threadname = main username = B password = AA

线程A休眠结束重新运行,赋值 password= BB,然后输出 username和password。

 

由此可知,当线程A调用对象Object 的synchronized 方法 X 时,A线程获得了 X 的方法锁,更准确的说是 对象Object锁。所以其他线程想要再执行 X 方法必须等线程A执行完X方法。但是其他线程可以调用对象Object中的其他 非synchronized 同步方法。
为了避免脏读,我们将setValue 也加上synchronized关键字变成同步方法,那么结果就变成:

main 将要执行 getValue() 方法
    setValue method threadname = Thread-0 username = B password = BB
    getValue method threadname = main username = B password = BB

getValue() 方法加上synchronized 之后,main线程调用必须等A线程执行完 setValue() 之后,也就是说 username和password 都已经被重新赋值了。然后main线程才能调用 getValue() 方法,不存在脏读环境。

6. synchronized 锁重入:当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。也就是说,在一个synchronized方法/块 的内部调用本类的其他synchronized 方法/块时,是永远可以得到锁的。

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 MyThread extends Thread {
    @Override
    public void run() {
        Service service = new Service();
        service.service1();
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
    /*
    运行结果:
    service1
    service2
    service3
     */
}

上面的例子非常清楚的描述了 可重入锁 的概念:自己可以再次获取自己的内部锁。其实,可重入锁也支持在父子类继承的环境中。

public class Main {
    public int i = 10;
    public synchronized void operateIMainMethod() {
        try {
            i--;
            System.out.println("main print i = " + i);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Sub extends Main {
    public synchronized void operateISubMethod() {
        try {
            while (i > 0) {
                i--;
                System.out.println("Sub print i=" + i);
                Thread.sleep(100);
                this.operateIMainMethod();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        Sub sub = new Sub();
        sub.operateISubMethod();
    }
}
public class Test2 {
    public static void main(String[] args) {
        MyThread2 myThread2 = new MyThread2();
        myThread2.start();
    }
    /*
    运行结果:
    Sub print i=9
    main print i = 8
    Sub print i=7
    main print i = 6
    Sub print i=5
    main print i = 4
    Sub print i=3
    main print i = 2
    Sub print i=1
    main print i = 0
     */
}

结果说明:当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。

7. 出现异常,锁自动释放

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 exceptionTime = " + System.currentTimeMillis());
                    Integer.parseInt("a");
                }
            }
        } else {
            System.out.println("Thread B run Time = " + System.currentTimeMillis());
        }
    }
}
public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
    }
}
public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        super.run();
        service.testMethod();
    }
}
public class Test {
    public static void main(String[] args) {
        try {
            Service service = new Service();
            ThreadA a = new ThreadA(service);
            a.setName("a");
            a.start();
            Thread.sleep(500);
            ThreadB b = new ThreadB(service);
            b.setName("b");
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /*
    运行结果:
    ThreadName = a run beginTime = 1506327473193
    ThreadName = a run exceptionTime = 1506327473361
    Exception in thread "a" java.lang.NumberFormatException: For input string: "a"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:492)
        at java.lang.Integer.parseInt(Integer.java:527)
        at com.leo.JavaImprove.chapter2.synchronizedFunc.package217.Service.testMethod(Service.java:15)
        at com.leo.JavaImprove.chapter2.synchronizedFunc.package217.ThreadA.run(ThreadA.java:16)
    Thread B run Time = 1506327473693
     */
}

线程a出现异常并释放锁,线程b进入方法正常打印,结论就是:出现异常的锁被自动释放了

8. 同步不具有继承性

 

 

public class Main {
    public synchronized void serviceMethod() {
        try {
            System.out.println("int MAIN 下一步 sleep BEGIN threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("int MAIN 下一步 sleep END threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Sub extends Main {
    public void serviceMethod() {
        try {
            System.out.println("int SUB 下一步 sleep BEGIN threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("int SUB 下一步 sleep END threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
            super.serviceMethod();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private Sub sub;
    public ThreadA(Sub sub) {
        super();
        this.sub = sub;
    }
    @Override
    public void run() {
        sub.serviceMethod();
    }
}
public class ThreadB extends Thread {
    private Sub sub;
    public ThreadB(Sub sub) {
        super();
        this.sub = sub;
    }
    @Override
    public void run() {
        sub.serviceMethod();
    }
}
public class Test {
    public static void main(String[] args) {
        Sub subRef = new Sub();
        ThreadA a = new ThreadA(subRef);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(subRef);
        b.setName("B");
        b.start();
    }
    /*
    运行结果:
    int SUB 下一步 sleep BEGIN threadName = A time = 1506330748908 }
    int SUB 下一步 sleep BEGIN threadName = B time = 1506330748909 } 线程A、B异步执行了 sub方法,BEGIN 输出
    int SUB 下一步 sleep END threadName = A time = 1506330753909
    int MAIN 下一步 sleep BEGIN threadName = A time = 1506330753909
    int SUB 下一步 sleep END threadName = B time = 1506330753910
    int MAIN 下一步 sleep END threadName = A time = 1506330758909
    int MAIN 下一步 sleep BEGIN threadName = B time = 1506330758909
    int MAIN 下一步 sleep END threadName = B time = 1506330763910
    ------sub.serviceMethod() 方法添加 synchronized 后----
    int SUB 下一步 sleep BEGIN threadName = A time = 1506330882904 }
    int SUB 下一步 sleep END threadName = A time = 1506330887904   } 同步执行,BEGIN、END 交替输出
    int MAIN 下一步 sleep BEGIN threadName = A time = 1506330887904
    int MAIN 下一步 sleep END threadName = A time = 1506330892905
    int SUB 下一步 sleep BEGIN threadName = B time = 1506330892905
    int SUB 下一步 sleep END threadName = B time = 1506330897905
    int MAIN 下一步 sleep BEGIN threadName = B time = 1506330897905
    int MAIN 下一步 sleep END threadName = B time = 1506330902906
     */
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值