synchronized同步方法和锁对象、脏读、可重入锁(二)

当多个线程调用同一个资源类中的同步方法和非同步方法,它们的执行顺序是什么?看如下代码

public class MyObject {

	synchronized public void methodA() {
		try {
			System.out.println("begin methodA threadName="
					+ Thread.currentThread().getName());
			Thread.sleep(5000);
			System.out.println("end endTime=" + System.currentTimeMillis());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	//非同步方法
	 public void methodB() {
		try {
			System.out.println("begin methodB threadName="
					+ Thread.currentThread().getName() + " begin time="
					+ System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("end");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
public class ThreadA extends Thread {

	private MyObject object;

	public ThreadA(MyObject object) {
		super();
		this.object = object;
	}

	@Override
	public void run() {
		super.run();
		object.methodA();
	}

}
public class ThreadB extends Thread {

	private MyObject object;

	public ThreadB(MyObject object) {
		super();
		this.object = object;
	}

	@Override
	public void run() {
		super.run();
		object.methodB();
	}
}

public class Run {

	public static void main(String[] args) {
		MyObject object = new MyObject();
		ThreadA a = new ThreadA(object);
		a.setName("A");
		ThreadB b = new ThreadB(object);
		b.setName("B");

		a.start();
		b.start();
	}
}

有上面可以知道 线程A先持有对象的锁,但是线程B完全可以异步调用非synchronized的方法。

如果把上面的methodB()也加上synchronized,则A线程在调用methodA()同步方法的时候获得了对象的锁。B线程在调用对象的其他同步方法则需要等待A线程执行完,再执行B线程。

下面演示脏读的情况

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 thread = new ThreadA(publicVarRef);
			thread.start();

			Thread.sleep(200);// 打印结果受此值大小影响
			//这里取值
			publicVarRef.getValue();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
public class PublicVar {

	public String username = "A";
	public String password = "AA";

	synchronized public void setValue(String username, String password) {
		try {
			this.username = username;
			Thread.sleep(5000);
			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() {
		System.out.println("getValue method thread name="
				+ Thread.currentThread().getName() + " username=" + username
				+ " password=" + password);
	}

如果getValue()方法加上synchronized 不会出现这种情况。


synchronized锁重入介绍

当一个线程得到一个对象的锁时,如果再次请求此对象的锁时 是 可以再次湖区该对象的锁的,这也证明在一个synchronized方法/块的内部调用本类中的其他synchronized的方法/块时,是永远可以得到锁的。如下代码验证。

public class Service {

	synchronized public void service1() {
		System.out.println("service1");
		service2();
	}

	synchronized public void service2() {
		System.out.println("service2");
		service3();
	}

	synchronized public void service3() {
		System.out.println("service3");
	}

}
public class MyThread extends Thread {
	@Override
	public void run() {
		//线程调用资源类中的同步方法
		Service service = new Service();
		service.service1();
	}

}
public class Run {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		t.start();
	}
}

再来说下“可重入锁”的概念:自己可以再次虎丘自己的内部锁。比如一个线程获取了某个对象的锁,此时这个对象的锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可以获取,就会造成死锁。

可重入锁支持父子类继承的环境中。如下代码

public class Main {

	public int i = 10;
	synchronized public void operateIMainMethod() {
		try {
			i--;
			System.out.println("main print i=" + i);
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
public class Sub extends Main {

	synchronized public 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 MyThread extends Thread {
	@Override
	public void run() {
		//子类操作资源类
		Sub sub = new Sub();
		sub.operateISubMethod();
	}

}
public class Run {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		t.start();
	}
}

当存在父子类关系时,子类完全可以通过可冲入锁,调用父类的同步方法。

这里还有一个重要的知识点就是:如果一个线程出现异常就会释放锁。

同步不具有继承性

public class Main {

	synchronized public 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 {

	@Override
	 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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

public class Test {

	public static void main(String[] args) {
		Sub subRef = new Sub();

		MyThreadA a = new MyThreadA(subRef);
		a.setName("A");
		a.start();

		MyThreadB b = new MyThreadB(subRef);
		b.setName("B");
		b.start();
	}

}

程序异步执行

子类重写的方法加上synchronized后 程序同步执行






猜你喜欢

转载自blog.csdn.net/qq_35400008/article/details/80349314