并发编程学习总结(七) :java中synchronized关键字使用详解(1)

上一篇中学习了显示锁ReentrantLock和其条件对象Condition的使用,下面小小的总结一下:

(1) 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码片段

(2) 锁可以管理试图进入被保护代码片段的线程

(3) 锁可以拥有一个或多个相关的条件对象

(4) 每个条件对象管理那些已经获得了锁,但还需要满足额外条件才能运行的线程

 

Lock和Condition接口为程序设计人员提供了高度的锁控制。实际上大多数情况下我们可以使用java的另一种锁机制。从jdk1.0开始,java 中每一个对象都有一个内部锁,每一个类都有一个类锁,如果一个方法用synchronized关键字声明,那么该方法称为“同步方法”,那么进入到该方法的线程将获得这个对象的锁,在该线程调用“同步方法”结束之前,其他试图调用该“同步方法”的线程将会阻塞。

 

同步方法的使用结构一般如下:

	public synchronized void methodName() {
		// method body
	}

这种结构等价于使用显示锁的如下结构:

	private ReentrantLock lock = new ReentrantLock();
	public void methodName() {
		lock.lock();
		try {
			// method body
		} finally {
			lock.unlock();
		}
	}

可以看到 使用synchronized关键字比使用内部锁ReentrantLock 要简洁的多,但是要理解同步方法的使用,你必须了解java的每一个对象都有一个内部锁,并且这个内部锁只用一个相关条件。由锁来管理那些试图进入同步方法的线程,由条件对象管理那些调用了条件对象wait() 方法的线程。

 

实际上synchronized关键字修饰的方法在多线程中有时会大大影响执行效率,比如当用synchronized关键字修饰Thread的run()方法时,此线程就无法调用本类中同样被synchronized关键字修饰的方法。此时我们应该减小加锁的粒度。

解决以上问题我们可以采用同步代码块的方式来使用synchronized关键字。使用结构如下:

	public void methodName() {
		// code
		synchronized(this) {
			// code
		}
		// code
    }

(1)synchronized关键字加锁

public class SynchronizedTest1 {

	// 使用synchronized关键字修饰的同步方法
	public synchronized void printNum() {
		for(int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final SynchronizedTest1 test = new SynchronizedTest1();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				test.printNum();
			}
		},"A");
		
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				test.printNum();
			}
		},"B");
		t1.start();
		t2.start();
	}

}

上述代码中线程A和线程B同时运行printNum()方法,若线程A首先进入同步方法,则线程A获得当前对象的锁,当线程A结束之前时间片到时,此时线程B开始调用同步方法,由于线程A未释放获得的锁,那么线程B即进入阻塞状态。这样就保证了线程A和线程B同步输出。

(2)synchronized关键字获得的锁也是可重入锁

public class ParentSynchronized {
	//父类同步方法
	public synchronized void parentMethod() {
		System.out.println("parent invoke");
	}
}


public class ChildrenSynchronized extends ParentSynchronized{

	// 子类同步方法 
	public synchronized void childrenMethod() {
		System.out.println("children invoke");
		parentMethod();
	}
	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		final ChildrenSynchronized cs = new ChildrenSynchronized();
		Thread t = new Thread(new Runnable() {
			public void run() {
				cs.childrenMethod();
			}
		});
		t.start();
	}

}


子类和父类中都有同步方法,子类方法 childrenMethod()中调用父类的 parentMethod()同步方法,线程调用 childrenMethod()方法 获得当前对象的锁,之后再调用父类的parentMethod()同步方法,由于两个同步方法使用的锁都是当前对象的锁,所有线程可以顺利的调用父类的同步方法。输出结果为如下:

children invoke
parent invoke


(3)synchronized关键字修饰类的静态方法

public class SynchronizedTest2 {
	
	// synchronized关键字修饰静态的方法 同步方法
	public synchronized static void printNum1(){ 
		for(int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
	
	// synchronized关键字使用类锁   同步代码块
	public static void printNum2(){ 
		synchronized(SynchronizedTest2.class) {
			for(int i = 0; i < 10; i++) {
				System.out.println(Thread.currentThread().getName() + " " + i);
			}
		}
	}
	
	// synchronized关键字修饰 同步方法  这里使用的是对象锁
	public synchronized void printNum3(){ 
		for(int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final SynchronizedTest2 test = new SynchronizedTest2();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				SynchronizedTest2.printNum1();
			}
		},"A");
		
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				SynchronizedTest2.printNum2();
			}
		},"B");
		
		Thread t3 = new Thread(new Runnable() {
			public void run() {
				test.printNum3();
			}
		},"C");
		t1.start();
		t2.start();
		t3.start();
	}
}


以上代码中 静态方法printNum1()  和 printNum2() 使用的都是类锁,方法printNum3()使用的是当前对象的锁。

我们开了三个线程A,B,C分别运行printNum1(),printNum2(),printNum3()方法 从结果中我们可以看到 线程A和线程B是始终同步的,线程C和线程A,B之间没有同步关系

输出的一种结果如下:











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值