高并发编程_Java线程基础 4.sychronized同步机制

高并发编程_Java线程基础 4.sychronized同步机制

之前在介绍线程的时候,讲到同一个进程内的多个线程,可以共享资源。这一特点,在为多线程编程带来方便的同时,也带来了一个不可忽视,在开发中经常遇到的问题,那就是多线程并发情况下,访问共享资源的安全问题。

Java语言提供了专门的机制以解决这个问题,那就是sychronized同步机制。使用sychronized关键字,修饰代码块。修饰方法、修饰类,为同步代码块、同步方法或者class对象添加锁,所有线程在访问的时候,首先需要获取锁资源,然后才可以访问共享资源。在sychronized修改的代码中,此时的并发线程(只是宏观上的体现,而在微观上依然是单线程运行则变成单线程串行运行,通过这种机制,可实现对共享资源的安全访问。当然同时也存在一个问题,那就是性能问题。因为在sychronized代码中,线程的串行执行,其他线程只能处于等待状态,只有当前获得锁资源的线程执行完成,其他竞争获得锁资源的线程才能继续执行,降低了系统的吞吐率,影响程序性能。并且同步在使用不当的情况下,会造成死锁,所以需慎用。

1.sychronized作用域

sychronized同步锁可分为两种类型,四种表现形式:

对象锁:对单个实例对象的独享内存的部分区域加锁

类锁:对整个类的共享内存的部分区域加锁

1.1 sychronized作用于非静态方法上

当sychronized作用于非静态方法上时,锁住的是当前实例对象,每个实例都拥有一把锁,调用任何一个sychronized修饰的方法时,都需要获得该实例的锁。

对于同一个实例对象来说,所有采用synchronized修饰的同步方法都会被锁定,任何时候,实例对象内的同步方法在同一时间只能被一个线程访问,因为在调用任何同步方法时,都需要获取势力对象的锁。其他非同步方法不受此限制,同一个实例对象,在访问同步方法时,仍然可以访问其他非同步方法。

package com.ryw.patd;

public class SychronizedMethod {
	
	    public static void main(String[] args) {
	        SyncDemo syncDemo1 = new SyncDemo();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	                syncDemo1.m1();
	            }
	        }, "thread1").start();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	            	syncDemo1.m2();
	            }
	        }, "thread2").start();
	    }
		

}
class SyncDemo {
	synchronized void m1() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(100);
				System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	synchronized void m2() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(100);
				System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


运行结果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec1-->thread2-->0
exec1-->thread2-->1
exec1-->thread2-->2
exec1-->thread2-->3
exec1-->thread2-->4

对于不同实例对象来说,所有被sychronized修饰的同步方法之间不会相互干扰。即实例对象A在访问同步方法A的时候,实例对象B仍然可以访问同步方法A。

package com.ryw.patd;

public class SychronizedMethod {
	
	    public static void main(String[] args) {
	        SyncDemo syncDemo1 = new SyncDemo();
			SyncDemo syncDemo2 = new SyncDemo();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	                syncDemo1.m1();
	            }
	        }, "thread1").start();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	                syncDemo2.m2();
	            }
	        }, "thread2").start();
	    }
		

}
class SyncDemo {
	synchronized void m1() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(100);
				System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	synchronized void m2() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(100);
				System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

输出结果:
m1-->thread1-->0
m2-->thread2-->0
m1-->thread1-->1
m2-->thread2-->1
m1-->thread1-->2
m2-->thread2-->2
m1-->thread1-->3
m2-->thread2-->3
m1-->thread1-->4
m2-->thread2-->4

1.2 sychronized作用于静态方法上

当sychronized作用于静态方法上时,锁住的是当前类对象(class),所有实例对象共用同一把锁。

当实例对象A访问同步静态方法A时,实例对象B仍然无法访问同步静态方法A。此时实例对象B处于等待对象,等待实例对象A访问结束,然后实例对象B才可以访问同步静态方法A。所有实例对象共用同一把锁,需要等待起一个实例对象执行完成,释放掉锁资源,其他实例对象方可执行。

package com.ryw.patd;

public class SychronizedMethod {
	
	    public static void main(String[] args) {
	        SyncDemo syncDemo1 = new SyncDemo();
	        SyncDemo syncDemo2 = new SyncDemo();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	                syncDemo1.m1();
	            }
	        }, "thread1").start();
	        new Thread(new Runnable() {
	            @Override
	            public void run() {
	            	syncDemo2.m2();
	            }
	        }, "thread2").start();
	    }
		

}
class SyncDemo {
	synchronized static void m1() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	synchronized static void m2() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

输出结果:
m1-->thread1-->0
m1-->thread1-->1
m1-->thread1-->2
m1-->thread1-->3
m1-->thread1-->4
m2-->thread2-->0
m2-->thread2-->1
m2-->thread2-->2
m2-->thread2-->3
m2-->thread2-->4

1.3 sychronized作用于普通代码块上

当sychronized作用于普通代码块上时,锁住的是当前实例对象。其他未加锁的代码不受同步机制的影响。原理同作用于非静态方法上。

对于同一个实例对象来说,所有采用synchronized修饰的同步代码块都会被锁定,任何时候,实例对象内的同步代码块在同一时间只能被一个线程访问,因为在调用任何同步代码块时,都需要获取实例对象的锁。

对于不同实例对象来说,所有被sychronized修饰的同步代码块之间不会相互干扰。即实例对象A在访问同步代码块A的时候,实例对象B仍然可以访问同步代码块A。

案例参考作用于非静态方法上的案例,此处不再写案例。

1.4 sychronized作用于静态代码块上

当sychronized作用于静态代码块时,锁住的是当前类对象。所有实例对象共用同一把锁。多个实例对象执行时,需要等待其他实例对象释放锁资源。

 

此次线程相关介绍就到这里,如有描述不当地方,请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值