并发编程(二)-访问共享资源

本文详细介绍了多线程环境中解决资源冲突的几种方法:synchronized同步方法与同步块、Lock锁等,并通过实例展示了如何正确使用这些机制来保证线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当代码中多个线程任务访问同一共享资源时,就会引发冲突,目前解决多线程冲突问题都是采用序列化访问共享资源的解决方案,即将共享资源放在某一代码块中并加锁,某一时刻只能有一个线程访问该代码块.
同步的规则:"如果你正在写一个变量,它接下来可能被另一个线程读取,或者你正在读取一个已经被其他线程改写的变量,那么你需要使用同步,并且读写线程必须使用相同的监视器同步".
1)Synchronized同步方法

共享资源在内存中以对象的形式存在,访问该对象的方法如果需要加锁,则添加synchronized关键字构成同步方法.当在一个线程中调用某对象的同步方法时,在同一时间其他线程中不能再调用该对象的其他同步方法或同步块;

public class SynchBrock {
	public static void main(String[] args) {
		Task t=new Task();
		for(int i=0;i<5;i++){
			new Thread(){
				public void run(){
					while(true){
						t.increment();
					}
				}
			}.start();
		}
		while(true){
			if(t.get()%2!=0){
				System.out.println(t.get());
				System.exit(0);
			}
		}
	}
}
class Task{
	public int i=0;
	public synchronized void increment(){
		i++;
		i++;
	}
	public synchronized int get(){
		return this.i;
	}
}
在上例中共享资源为t.i,访问共享变量的共有两个方法increment()和get(),根据同步规则,两个方法都需要同步.同一时间只会有一个线程访问其中的一个方法,因此不会出现奇数的情况.

2)Synchronized同步块
使用Synchronized关键字创建同步块(临界区)时需要给定一个在其上进行同步的对象,最合理的是使用调用当前方法的对象即:synchronized(this),此时在其他线程中就不能访问该对象的其他synchronized方法或临界区;

public class SynchBrock {
	public static void main(String[] args) {
		Task t=new Task();
		for(int i=0;i<5;i++){
			new Thread(){
				public void run(){
					while(true){
						t.increment();
					}
				}
			}.start();
		}
		while(true){
			if(t.get()%2!=0){
				System.out.println(t.get());
				System.exit(0);
			}
		}
	}
}
class Task{
	public int i=0;
	public void increment(){
		synchronized(this){
			i++;
			i++;
		}
	}
	public synchronized int get(){
		return this.i;
	}
}
上例中将increment()方法中关键代码部分改成synchronized同步块,由于此时同步块同步的对象为this,因此get()同步方法也会在任务之间互斥,因此同一时间只会有一个线程访问其中的一个方法;
假如synchronized同步块在另外一个对象上同步,那么会出现同一时间两个线程分别访问increment()和get()方法;

public class SynchBrock {
	public static void main(String[] args) {
		Task t=new Task();
		for(int i=0;i<5;i++){
			new Thread(){
				public void run(){
					while(true){
						t.increment();
					}
				}
			}.start();
		}
		while(true){
			if(t.get()%2!=0){
				System.out.println(t.get());
				System.exit(0);
			}
		}
	}
}
class Task{
	public int i=0;
	Object ob=new Object();
	public void increment(){
		synchronized(ob){
			i++;
			i++;
		}
	}
	public synchronized int get(){
		return this.i;
	}
}
3)Lock同步块
Lock对象必须被显示的创建,锁定和释放.使用时需要注意两点:1)必须在finally中释放锁;2)return语句必须在try语句中,确保unlock不会过早发生.
public class SynchBrock {
	public static void main(String[] args) {
		Task t=new Task();
		for(int i=0;i<5;i++){
			new Thread(){
				public void run(){
					while(true){
						t.increment();
					}
				}
			}.start();
		}
		while(true){
			if(t.get()%2!=0){
				System.out.println(t.get());
				System.exit(0);
			}
		}
	}
}
class Task{
	public int i=0;
	private Lock lock=new ReentrantLock();
	public void increment(){
		lock.lock();
		try{
			i++;
			i++;
			return;
		}
		finally{
			lock.unlock();
		}	
	}
	public synchronized int get(){
		return this.i;
	}
}
多线程访问共享资源则需要加锁,加锁必须明白要对谁加锁,同步方法是对该方法的对象进行加锁,同步块必须指出要加锁的对象,当访问某加锁对象的同步方法时,同一时刻其他线程中不能访问该对象的其他同步方法或同步块.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值