java多线程学习(二)

本篇主要说明synchronized方法的一些属性

总结:

1,局部变量是线程安全的

2,同步是针对同一个对象,如果是同一类的不同实例,是异步的,不需要同步

同一对象的不同synchronized方法是同步调用的,而同一类的非syn方法可以异步调用,

3,锁具有可重入性:获取对象锁后可以直接获取同类及其父类的其他syn方法的锁
4,异常发生会释放锁
5,syn不会被继承:子类重写父类的syn方法,不会继承其syn属性

1,成员变量是非线程安全的,而局部变量永远是线程安全的

示例:

package chapter2.t1;

public class ClassA {
	public void modifyI(String username){
		try{
			int i=0;
			if("a".equals(username)){
				i=100;
				System.out.println("a set over");
				Thread.sleep(1000);
			}else{
				i=200;
				System.out.println("b set over");
			}
			System.out.println(username+": i="+i);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
}
package chapter2.t1;

public class ThreadA extends Thread {
	private ClassA a;
	public ThreadA(ClassA a){
		super();
		this.a=a;
	}
	public void run(){
		super.run();
		a.modifyI("a");
	}
}

package chapter2.t1;

public class ThreadB extends Thread {
	private ClassA a;
	public ThreadB(ClassA a){
		super();
		this.a=a;
	}
	public void run(){
		super.run();
		a.modifyI("b");
	}
}

package chapter2.t1;
/*
 * 演示局部变量(方法内变量的线程安全性,只有成员变量才会有线程安全问题.
 */
public class Run {

	public static void main(String[] args) {
		ClassA a=new ClassA();
		ThreadA ta=new ThreadA(a);
		ThreadB tb=new ThreadB(a);
		ta.start();
		tb.start();
//		输出为:
//			a set over
//			b set over
//			b: i=200
//			a: i=100

//		new Run().test();
	}
	public void test(){
		//方法内部的变量只能用final修饰,因为局部变量没有权限一说,方法外是不能访问的
//		private int a=0;
//		System.out.println(a);
	}

}

如果将ClassA中的i变量变为成员变量,那么输出为:i的值呗b方法覆盖了

		b set over
		a set over
		b: i=200
		a: i=200

如果i是成员变量,实现线程安全的方法是对modifyI方法加上synchronized修饰

package chapter2.t3;
/*
 * 和t2的非线程安全相比,这边对方法加上synchronize,也能实现成员变量的线程安全
 */
public class ClassA {
	private int i=0;
	synchronized public void modifyI(String username){
		try{
			if("a".equals(username)){
				i=100;
				System.out.println("a set over");
				Thread.sleep(1000);
			}else{
				i=200;
				System.out.println("b set over");
			}
			System.out.println(username+": i="+i);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
}

两个线程访问同一对象的同步方法是是线程安全的,但是会造成线程阻塞状态

2,同步与异步的区别

将上面例子的i修改为成员变量,并且用synchronized修饰方法,但是在run中传入两个对象如下:

package chapter2.twoObjectTwoLock;
/*
 * 演示同步(synchronized)和异步(asynchronized)的区别
 * 同一个对象,实现的是同步,多个对象实现的是异步
 * synchronized加锁的对象是类实例,如果是同一个实例,会出现等待上一个线程释放锁
 * 如果是多个对象,那么JVM会对不同的对象创建不同的锁,没有等待,也就是异步
 */
public class Run {

	public static void main(String[] args) {
		ClassA a=new ClassA();
		ClassA b=new ClassA();
		ThreadA ta=new ThreadA(a);
		ThreadB tb=new ThreadB(b);
		ta.start();
		tb.start();
//		输出为:
//		a set over
//		b set over
//		b: i=200
//		a: i=100
//		new Run().test();
	}

}

b线程没有等待a,直接输出了,并且a的值也没有被覆盖,所以异步是不同对象获得不同锁,同步是同一对象获得同步方法的同一个锁,

同一个对象的多个方法的同步关系:

示例:

package chapter2.synchroniezedMethodLockObject2;

public class MyObject {
//	public void methodA(){
	synchronized public void methodA(){
		try{
			System.out.println("begin methodA threadname= "+
					Thread.currentThread().getName()+
					" begin time="+System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("end methodA,time="+System.currentTimeMillis());
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
//	public void methodB(){
	synchronized public void methodB(){
		try{
			System.out.println("begin methodB threadname= "+
		Thread.currentThread().getName()+
		" begin time="+System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("end methodB,time="+System.currentTimeMillis());
			
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
package chapter2.synchroniezedMethodLockObject2;

public class ThreadA extends Thread {
	private MyObject myobj;
	public ThreadA(MyObject obj){
		super();
		this.myobj=obj;
	}
	@Override
	public void run(){
		super.run();
		myobj.methodA();
		
	}
}

package chapter2.synchroniezedMethodLockObject2;

public class ThreadB extends Thread {
	private MyObject myobj;
	public ThreadB(MyObject obj){
		super();
		this.myobj=obj;
	}
	@Override
	public void run(){
		super.run();
		myobj.methodB();
		
	}
}
package chapter2.synchroniezedMethodLockObject2;
/*
 * 演示两个线程调用同一个对象的不同方法,其中一个是同步的方法,
 * 结论是threadB可以异步调用非同步方法methodb,
 * 如果methodB也是同步方法,那么thradb需要等到threada线程结束才能执行同一个对象的另一个synchronized方法
 */
public class Run {

	public static void main(String[] args) {
		MyObject obj=new MyObject();
		ThreadA ta=new ThreadA(obj);
		ta.setName("a");
		ThreadB tb=new ThreadB(obj);
		tb.setName("b");
		ta.start();
		tb.start();
//		输出为:
//			begin methodB threadname= b begin time=1525254799564
//			begin methodA threadname= a begin time=1525254799564
//			end methodA,time=1525254804565
//			end methodB,time=1525254804565
//		如果methodB也是同步方法,输出为:
//			begin methodA threadname= a begin time=1525255141141
//			end methodA,time=1525255146142
//			begin methodB threadname= b begin time=1525255146143
//			end methodB,time=1525255151143

	}

}

结论:

A线程先持有object对象的lock锁,B线程可以以异步的方式调用object对象的非synchronized方法,

A线程先持有object对象的lock锁,,B线程如果调用object对象的synchronized方法,需要等待,也就是同步

3,可重入锁

关键字synchronized拥有锁重入的功能:

当一个线程得到一个对象锁之后,再次请求此对象锁是可以再次得到该对象锁的,也就是说,在一个synchronized方法/块的内部调用本类的其他synchronized方法/块总是可以得到的,另外可重入锁也支持在父子类继承的场景,也就是获得子类锁的线程可以直接获得父类的锁

可重入锁的作用是防止死锁的出现,看下面代码:


package chapter2.synLockIn_2;

/*
 * 本方法演示线程获得子类对象的锁,可以通过可重入锁的特性获得父类的锁
 */
public class Run {

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

}
package chapter2.synLockIn_2;

public class MyThread extends Thread {
	@Override
	public void run(){
		Sub sb=new Sub();
		sb.operateISubMethod();
	}
}
package chapter2.synLockIn_2;

public class Main {
	public int i=11;
	synchronized public void operateIMainMethod(){
		try{
			i--;
			System.out.println("main print i="+i);
			Thread.sleep(1000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
}
package chapter2.synLockIn_2;

public class Sub extends Main {
	synchronized public void operateISubMethod(){
		try{
			while(i>0){
				i--;
				System.out.println("sub print i="+i);
				Thread.sleep(1000);
				this.operateIMainMethod();
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
}
输出为:sub print i=10
main print i=9
sub print i=8
main print i=7
sub print i=6
main print i=5
sub print i=4
main print i=3
sub print i=2
main print i=1
sub print i=0
main print i=-1

4,异常释放锁

线程获得对象锁后,如果执行过程中发生异常,会释放锁

示例:

package chapter2.throwExceptioinNoLock;
/*
 * 演示在a线程获取到对象锁后,执行过程发生异常(不论是否捕获异常),会释放锁,b线程得以执行
Thread name=a run begin time=1525455640331
Thread name=a run exception time=1525455641371
NumberFormatException 被捕获
ThreadB run time=1525455641371
 */
public class Run {

	public static void main(String[] args) throws InterruptedException {
		Service sv=new Service();
		ThreadA ta=new ThreadA(sv);
		ThreadB tb=new ThreadB(sv);
		ta.setName("a");
		tb.setName("b");
		Thread.sleep(500);
		ta.start();
		tb.start();
	}

}
package chapter2.throwExceptioinNoLock;

public class ThreadA extends Thread {
	private Service sv=new Service();
	public ThreadA(Service sv) {
		this.sv=sv;
	}
	@Override
	public void run(){
		sv.testMethod();
	}
}
package chapter2.throwExceptioinNoLock;

public class ThreadB extends Thread {
	private Service sv=new Service();
	public ThreadB(Service sv) {
		this.sv=sv;
	}
	@Override
	public void run(){
		sv.testMethod();
	}
}
package chapter2.throwExceptioinNoLock;

public class Service {
	synchronized public void testMethod(){
		
		if(Thread.currentThread().getName().equals("a")){
			try{
				System.out.println("Thread name="+Thread.currentThread().getName()+
						" run begin time="+System.currentTimeMillis()
						);
				while(true){
					if((""+Math.random()).substring(0, 8).equals("0.123456")){
						System.out.println("Thread name="+Thread.currentThread().getName()+
								" run exception time="+System.currentTimeMillis()
								);
						Integer.parseInt("a");
					}
				}
			}catch(NumberFormatException e){
				System.out.println("NumberFormatException 被捕获");
			}
		}else{
			System.out.println("ThreadB run time="+System.currentTimeMillis());
		}
	}
}

5,同步不具有继承性

synchronized方法被子类重写后,同步属性不会被继承,子类同步由子类方法是syn决定

示例:

package synNotExtends;
/*
 * 演示子类非syn方法重写父类syn方法,子类方法不会继承父类的syn属性,
 * 线程b和线程a同时调用子类的非syn方法,但是在执行父类syn方法时是同步的,b需要等待a执行完毕才能进入
 * in Sub thread name=b sleep begin time=1525456850027 第一行第二行显示a,b线程同时调用子类方法
in Sub thread name=a sleep begin time=1525456850027
in Sub thread name=b sleep end time=1525456855027
in Sub thread name=a sleep end time=1525456855027 同时sleep结束调用父类syn方法
in main thread name=a sleep begin time=1525456855028 a先进入,b等待
in main thread name=a sleep end time=1525456860028
in main thread name=b sleep begin time=1525456860029 b等待a sleep后再进入
in main thread name=b sleep end time=1525456865029
 */
public class Run {

	public static void main(String[] args) {
		Sub sb=new Sub();
		ThreadA ta=new ThreadA(sb);
		ThreadB tb=new ThreadB(sb);
		ta.setName("a");
		tb.setName("b");
		ta.start();
		tb.start();
	}

}
package synNotExtends;

public class Main {
	synchronized public void serviceMethod(){
		try {
			System.out.println("in main thread name="+Thread.currentThread().getName()
					+" sleep begin time="+System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("in main thread name="+Thread.currentThread().getName()
					+" sleep end time="+System.currentTimeMillis());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
package synNotExtends;

public class Sub extends Main{
	@Override
	public void serviceMethod(){
		try {
			System.out.println("in Sub thread name="+Thread.currentThread().getName()
					+" sleep begin time="+System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("in Sub thread name="+Thread.currentThread().getName()
					+" sleep end time="+System.currentTimeMillis());
			super.serviceMethod();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
package synNotExtends;

public class ThreadA extends Thread {
	private Sub sb;
	public ThreadA(Sub sb){
		super();
		this.sb=sb;
	}
	@Override
	public void run(){
		sb.serviceMethod();
	}
}
package synNotExtends;

public class ThreadB extends Thread {
	private Sub sb;
	public ThreadB(Sub sb){
		super();
		this.sb=sb;
	}
	@Override
	public void run(){
		sb.serviceMethod();
	}
}

参考:<java多线程编程核心技术>



猜你喜欢

转载自blog.csdn.net/qq467215628/article/details/80160645