线程的共享(synchronized)

本文深入探讨Java中线程的共享与同步机制,重点讲解synchronized关键字的使用场景及其实现原理。通过实例演示了如何利用synchronized修饰符确保线程安全,避免并发操作时的数据不一致问题。

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

线程的共享(synchronized)

synchronized :修饰方法,或是作用于块,但是无论如何,我们都应该视其为一个禁入条件,
也就是说,synchronized应该使用的场景是:当一个方法有可能被并行执行时,
而其内部有对数据进行增删改的操作时,需要被保证其进入条件,
此时可选择使用,synchronized代码块 或是 synchronized 方法。
形象生动地:
		我们可以视其synchronized为一个锁,
		其包裹内容视作房间,整体看做是外面带有锁的房间

“当一个方法有可能被并行执行时”:
	这意味着:
		如果方法为一个实例方法,那么当我们使用该类同一个对象,
		并且有可能并行地执行该实例方法时,我们才可能使用synchronized
		如果我们是串行操作,又或者对于每一个对象,使用其自身的这个实例方法,
		那么我们是不需要添加synchronize。
		同理,当方法为类方法时,也作同样考虑,但此时主语为该类本身,而非其对象行为

“作用于块”:	
	一般而言将对象this当资源:
	synchronized(this){
		...
	}
“修饰方法”:
	public  synchronized void go()  {
		...
	}

举个例子说明:
当同一对象通过自身实例方法对数值做出加减时,要保证仅有这个对象同时仅进入一个。

1.创建Callable接口实现类,重写call,定义synchronized方法,和普通方法

class CallThread implements Callable<Integer>{
	private Mediation mediation;
	public boolean flag = true;
	public CallThread(Mediation mediation) {
		this.mediation = mediation;
	}
	
	@Override
	public Integer call() throws Exception {
		while(flag) {
			if(mediation.num > 0) {
				try {
					go1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}	
			else {
				break;
			}
		}
		return Integer.valueOf(5);
	}

go():

public  synchronized void go() throws InterruptedException {
	String aString;
	if(mediation.num % 2 == 0) {
		aString = Thread.currentThread().getName()  + " has num"+": "+mediation.num--;
		System.out.println(aString);
	}
}

go1():

public void go1() throws InterruptedException {
	String aString;
	if(mediation.num % 2 == 0) {
		aString = Thread.currentThread().getName()  + " has num"+": "+mediation.num--;
		System.out.println(aString);
	}
}

go2():

public void go2() throws InterruptedException {
	synchronized (Integer.valueOf(mediation.num)) {
		String aString;
		if(mediation.num % 2 == 0) {
			aString = Thread.currentThread().getName()  + " has num"+": "+mediation.num--;
			System.out.println(aString);
		}
	}
}		

go3():

public void go3() throws InterruptedException {
	synchronized (Integer.valueOf(mediation.num)) {
		String aString;
		if(mediation.num % 2 == 0) {
			aString = Thread.currentThread().getName()  + " has num"+": "+mediation.num--;
			System.out.println(aString);
		}
	}
	
}	
} //这个括号是整个CallThread类的	

2.通过使用Executors.newCachedThreadPool()创建ExecutorService类对象,
再由ExecutorService类的对象,submit()同一个callable实现类的对象,
内部调用非synchronized方法,可以观察到结果显示

class Mediation{
	public int num = 500;
}

主进程:

Mediation mediation = new Mediation();
CallThread callThread = new CallThread(mediation);
ExecutorService eService = Executors.newCachedThreadPool();
Future<Integer> future  = eService.submit(callThread);
Future<Integer> future1  = eService.submit(callThread);
Thread.sleep(1000);
callThread.flag = false;
System.out.println(future.get(1000,TimeUnit.MILLISECONDS));
System.out.println(future1.get(1000,TimeUnit.MILLISECONDS));
eService.shutdown();
if(!eService.awaitTermination(8*1000, TimeUnit.MILLISECONDS)){  
     eService.shutdownNow();  
 }

3.我们可以观察到输出结果是:

go1():且发生了get()引发的TimeoutExcepiton
	pool-1-thread-1 has num: 499
	pool-1-thread-1 has num: 498
	pool-1-thread-2 has num: 500
可以推测出来:thread2,thread1同时到达判断%2的位置,并且同时判断成功
但是thread2先完成了减一操作,thread1紧追其后,但此时1获得的已经是减了1的499,
而499是不满足于先前的%2判断的,这里是一个错误。另一个错误是,
thread1完成了499的减一操作后,再一次使数值%2判断成功,此时,
不管是thread1,thread2,都有机会在间隙中进入运行,并成功,
但是如果再往后,并非都能继续借助间隙来继续执行,
而应该是会存在时间片的长度限制。

4.尝试使用go(),即使用了synchronized包裹的go1函数,观察结果:

	pool-1-thread-1 has num: 500
	5
	5
后面的5,5,为返回值,第一条非常符合,应该threa1,thread2,仅有1个进去运行,
得到了500,减为499,thread1,或是thread2没有办法在499的情况下进入运行,
因此仅仅为一个500。

5.尝试使用synchronized(this)代码块:–go2()

	pool-1-thread-1 has num: 500
	5
	5
输出结果和4一致,是正确的。

6.尝试使用:锁定其他资源的go3(),观察其输出结果
synchronized(Integer.valueOf(mediation.num)

	pool-1-thread-2 has num: 500
	pool-1-thread-2 has num: 498
	pool-1-thread-1 has num: 499
输出结果和3一致,是错误的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值