synchronized块

最近阅读网上关于synchronized块的资料,说法也不尽相同,经过自己代码测试,以下仅为个人理解,如有错误,麻烦指出问题所在,谢谢~

synchronized块参数表示的是以什么为锁,参数可以为固定字符串,this,数组对象等等,但不能为int等8种基本数据类型,只有锁相同,才能导致等待。

public class SynchronizedKuai{
	
	private static int i = 0;
	private static byte[] lock = new byte[0];;
	public void A(){
		synchronized(lock){
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
			i++;
			System.out.println("调用了++,当前值为:"+i);
			for (int j = 0; j < 1000000000; j++) {
				
			}
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
				i++;
				System.out.println("调用了++,当前值为:"+i);
		}
	}
	public void B(){
		synchronized(lock){
			i--;
			System.out.println("======================================================");
			System.out.println("调用了--,当前值为:"+i);
		}
	}
	
	public static void main(String[] args) {
		Sub sub = new Sub();
		Add add = new Add();
		Thread th1 = new Thread(sub);
		th1.start();
		Thread th2 = new Thread(add);
		th2.start();
	}
}
class Sub implements Runnable{
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			SynchronizedKuai synchronizedKuai = new SynchronizedKuai();
			synchronizedKuai.B();
		}
	}
}

class Add implements Runnable{
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			SynchronizedKuai synchronizedKuai = new SynchronizedKuai();
			synchronizedKuai.A();
		}
	}
}

如上例子,对于两个线程可以锁住。然而对于如下例子,却锁不住。

public class SynchronizedKuai{
	
	private static int i = 0;
	public void A(){
		synchronized(this){
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
			i++;
			System.out.println("调用了++,当前值为:"+i);
			for (int j = 0; j < 1000000000; j++) {
				
			}
			System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
				i++;
				System.out.println("调用了++,当前值为:"+i);
		}
	}
	public void B(){
		synchronized(this){
			i--;
			System.out.println("======================================================");
			System.out.println("调用了--,当前值为:"+i);
		}
	}
	
	public static void main(String[] args) {
		Sub sub = new Sub();
		Add add = new Add();
		Thread th1 = new Thread(sub);
		th1.start();
		Thread th2 = new Thread(add);
		th2.start();
	}
}
class Sub implements Runnable{
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			SynchronizedKuai synchronizedKuai = new SynchronizedKuai();
			synchronizedKuai.B();
		}
	}
}

class Add implements Runnable{
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			SynchronizedKuai synchronizedKuai = new SynchronizedKuai();
			synchronizedKuai.A();
		}
	}
}

两个例子看似相同,但是其实synchronized快的锁其实并不相同,当使用的是this时,锁为对象,它是两个对象,对同一数据操作不会等待,而使用byte长度为0的数组开销是最小的,如果不使用静态的byte数组,对于两个不同对象的线程,是两个不同的byte数组,两个线程线程也无法锁住synchronized快。


以下部分为学习时看到不错的经验。

有一些技巧可以让我们对共享资源的同步访问更加安全:

1. 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。

2. 如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。


注意:对于使用clone()方法的使用:实例调用clone方法就需要让此类实现Cloneable接口,API里面还有句话是:如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常,这便是“合法”的含义。 但请注意,Cloneable接口只是个标签接口,不含任何需要实现的方法,就像Serializable接口一样。例如:

class Employee implements Cloneable
{
   public Employee clone() throws CloneSupportedException
   {
      return (Employee)super.clone();
   }
}


你看到clone()是Object的方法,而Object是所有类的父类,就认为可以用Object调用 clone是么?
但不能使用Object调用clone()方法,原因如下:

1.从你写法本身来看,Object.clone()肯定不行,因为Object是类, 而clone()是成员方法,所以不能用Object.clone();
2.那么是不是可以先Object obj=new Object();再obj.clone();或者Object.class.newInstance().clone()呢?还是不行。
  clone()对Object类对象本身是不可见的。所以你会发现找不到clone()方法
  原因是:clone()方法是protected访问权限
  方法原型:protected native java.long.Object clone() throws java.long.CloneNotSupportedException
  而protected权限的含义是:只对同一包中的类和不同包中的子类及间接子类可访问,换句话说就是不同包中的非子类或间接子类不能访问,包含了默认的包访问权限。
  这个地方容易混淆哦
  你可能会问任何类都是Object的子类为什么不能访问呢,对,任何类是他的子类也继承了clone()方法
  但请注意,你那句代码clone()的调用者是谁?是Object本身,即Object对象,正如我们所说,“不同包中的非子类或间接子类不能访问”,这里面也包括了父类本身,你调用者是父类,在不同包中,protected成员对父类不可见哦,所以根本不能用object对象调用clone()
   
  综上,因为在不同包中父类本身不能调用,也找不到protected方法 ,故无法在代码中使用object直接调用clone()方法,有点难理解,关键在对protected的理解上,因为protected除了本身在继承上的访问权限外,还包含了包访问权限~


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值