目录
Synchronized使线程安全
线程安全与非线程安全是使用多线程的时候都会遇到的经典的问题。我所理解的“非线程安全”也就是多个线程同时访问同一对象中的实例变量所产生一些脏读的现象。而线程安全就是以获得的实例变量的值都是经过同步处理的,从而不会产生脏读的现象。其中使结果线程安全的最简单的方法就是加上一个重量级的锁,用synchronized关键字进行修饰。
以下这种情况将会出现非线程安全的情况,代码如下
package com.huawei.bes.demo;
/**
* Created by lenovo12 on 2018/11/5.
*/
public class SharedNum {
private int num = 0;
public void addI(String username) throws InterruptedException {
if("a".equals(username))
{
num = 100;
System.out.println("a is over");
}
else
{
num = 200;
System.out.println("b is over");
}
System.out.println(username + "num = " + num);
}
public static void main(String[] args) {
SharedNum sharedNum = new SharedNum();
ThreadA a = new ThreadA(sharedNum);
ThreadB b = new ThreadB(sharedNum);
a.start();
b.start();
}
}
class ThreadA extends Thread{
private SharedNum sharedNum ;
public ThreadA(SharedNum sharedNum)
{
this.sharedNum = sharedNum ;
}
@Override
public void run()
{
try {
sharedNum.addI("a");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SharedNum sharedNum ;
public ThreadB(SharedNum sharedNum)
{
this.sharedNum = sharedNum ;
}
@Override
public void run()
{
try {
sharedNum.addI("b");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下,产生了脏读的现象
此时若在SharedNum类中的addI方法进行synchronized修饰,则有结果发现是同步进行的,线程安全了。
synchronized对象锁
构建一个BESObject类,里面有两个方法methodA 与 methodB 其中只有methodA是同步的。然后启动两个线程执行分别执行这两个方法,代码如下。
package com.huawei.bes.demo;
/**
* Created by lenovo12 on 2018/11/5.
*/
public class BESObject {
public synchronized void methondA() throws InterruptedException
{
System.out.println("begin methodA Thread Name is " + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("end method A " + System.currentTimeMillis());
}
public void methondB() throws InterruptedException
{
System.out.println("begin methodB Thread Name is " + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("end method B " + System.currentTimeMillis());
}
public static void main(String[] args) {
BESObject besObject = new BESObject();
ThreadC c = new ThreadC(besObject);
ThreadD d = new ThreadD(besObject);
c.setName("C");
d.setName("D");
c.start();
d.start();
}
}
class ThreadC extends Thread{
private BESObject besObject;
public ThreadC(BESObject besObject)
{
this.besObject = besObject;
}
@Override
public void run()
{
try {
besObject.methondA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadD extends Thread{
private BESObject besObject;
public ThreadD(BESObject besObject)
{
this.besObject = besObject;
}
@Override
public void run()
{
try {
besObject.methondB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下:
接着我们将methodB也使用“synchronized” 关键字进行修饰,运行结果如下
所以这个结果我们可以得出一个结论了:
- 线程C先持有object对象的锁,D线程还是可以异步的方式调用object对象中的非synchronized方法
- 线程C先持有object对象的锁,D线程如果在这个时候调用object对象中的synchronized方法就会同步了。
synchronized 拥有锁重入的功能
可重入锁的就是:自己可以再次获取自己的内部锁,比如有一个线程获得了某个对象锁,这个时候这个对象锁还没有释放,当它再次想要获取这个对象的锁的时候还是可以获取的,如果synchronized不可重入锁的话就会造成死锁。下面代码解释一切
package com.huawei.bes.demo;
class MyThread extends Thread{
@Override
public void run()
{
SynTest service = new SynTest();
service.service1();
}
}
public class SynTest {
public synchronized void service1(){
System.out.println("服务1");
service2();
}
public synchronized void service2(){
System.out.println("服务2");
service3();
}
public synchronized void service3(){
System.out.println("服务3");
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
运行结果如下,由此可以得出结论 “synchronized”拥有可重入锁