Java 中的Sychronized关键字 和线程

本文详细解析了Java多线程中的synchronized关键字的使用方式及其背后的实现机制,通过一个具体的小程序实例,展示了synchronized如何确保线程间的互斥访问,以及其对线程执行流程的影响。文中还探讨了线程执行时间分配的不确定性,以及getValue()方法的执行不受evenIncrement()方法内部操作的直接影响。

   这两天看到<<Thinking in java>> 多线程,里面有个syncrhonized 关键字,以前也稍微接触过一点。

    好像挺令人迷惑的。

     写了个小程序测试了一下:

       程序中,通过getValue()观察i 的变化,来看线程 和Sychronized 关键字之间是怎么工作的。evenIncrement()保证i 是偶数,当i为奇数时,打印 i  的值,并退出。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class AtomTest implements Runnable {
	private int i = 0;
	public  int getValue(){System.out.println("Got I!"+ i);return i ;}
	private synchronized void evenIncrement (){i++;i++;}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ExecutorService exec = Executors.newCachedThreadPool();
		AtomTest at = new AtomTest();
		exec.execute(at);
		while(true){
			int val  = at.getValue();
			if(val %2!=0){
				System.out.println(val+ "   i = "+at.getValue()+"  "+at.getValue() );
				System.exit(0);
			}
		}
	}

	@Override
	public void run() {
		while(true){
			evenIncrement();
		}
	}

}

运行结果为:

Got I!0
Got I!5234
Got I!6662
Got I!7647
5999   i = 7521  8088

有意思的是getValue()得到的i 可以不是偶数,否则就不会打印出5999这个奇数。

按道理不是的啊,evenIncrement() 每次都是让变量自增2的呀,i不可能是奇数的。可结果明明就打印出了奇数。

惟一的可能是当evenIncrement()中执行第一个i++时,getValue()获得了i的值。

那Sycrhonized 关键字是干什么的?

应该这样理解: 加了  Sycrhonized 关键字的方法体,里面的元素是“锁着的”,同时该方法只有获得钥匙(可能是java 里的某些权限)才能访问里面的资源(比如这里的 i),而且钥匙只有一把,一次只能由一个方法获得。

  举个栗子:  所谓“一山不容二虎”,“一个花果山只能有一个美猴王”。

                     

                        这里的 i 就相当于 “花果山”,两个美猴王就是加了Syncrhonized的方法。在同一个时间内,只有一个 猴王统治花果山。只有等一个猴王被赶走了,下一个美猴王才                        能来。

                       后来,(另一个方法getValue() )沙僧也来了。沙僧怎么能进花果山啊?沙僧说:“我又不是声明了Sycrhonized 的方法,当然随时都可以来(访问i 的数值)了。”

           也就是说getValue()方法可以随时访问 i,与evenIncrement ()是否完成i++两次操作没有任何关系。于是,打印出的i 就有可能是奇数。

那么,当我们把evenIncrement ()加了Sychronized 之后会怎么样了?答案是:没有输出,程序永远执行下去。这是显而易见的。


输出结果还有一个有意思的现象,可以帮助我们理解线程的一些小原理。

           为什么结果往往不是 1而是其他的,比如5999,按道理说第一次getValue(),应该是1才对啊。

         这就是cpu分配时间片造成的,当程序执行到evenIncrement()时,cpu有时候给它很多时间,它执行了不只一次,所以通常不是1。

而且

System.out.println(val+ "   i = "+at.getValue()+"  "+at.getValue() );
val 的输出值和 at.getValue()+" "+at.getValue() 都不相同,而且两个getValue()获得的值通常也不相同,这就更说了CPU 分配时间的不固定。

      当执行 输出val 后,Cpu也许把时间分配给了evenIncrement(),这样此时i 完成了很多次自加,它的值就不会等于val 的值。


### 回答1: synchronizedreentrantlock都是Java中用于实现线程同步的机制。 synchronized是Java中最基本的同步机制,它可以用于方法或代码块级别的同步。当一个线程进入synchronized代码块时,它会尝试获取锁,如果锁已经被其他线程持有,则该线程会被阻塞,直到锁被释放。synchronized机制是Java中最常用的同步机制之一,但它有一些限制,例如无法中断正在等待锁的线程,也无法尝试获取锁而不阻塞。 reentrantlock是Java中另一种同步机制,它提供了更多的灵活性控制。与synchronized不同,reentrantlock可以中断正在等待锁的线程,也可以尝试获取锁而不阻塞。此外,reentrantlock还提供了一些高级功能,例如公平锁可重入锁。但是,reentrantlock的使用比synchronized更复杂,需要手动获取释放锁,并且需要注意避免死锁等问题。 总的来说,synchronized是Java中最基本的同步机制,适用于大多数情况。而reentrantlock则提供了更多的灵活性控制,适用于一些特殊的场景。 ### 回答2: synchronizedReentrantLock是Java中用于实现线程同步的两种机制。 synchronized是Java中最基本的线程同步机制,它使用了内置的监视器锁(也称为对象锁或互斥锁)来控制线程的访问。synchronized关键字可以用于方法或代码块,并且是隐式锁。当线程执行到synchronized关键字时,会自动获取锁,执行完后会自动释放锁。synchronized关键字确保了在同一时间只有一个线程可以访问被锁定的代码或方法,从而保证了线程的安全性。然而,synchronized关键字也有一些缺点,比如无法获得锁时会一直阻塞等待释放锁。 ReentrantLock是Java中的另一种线程同步机制,它是通过显示锁来实现的。ReentrantLock提供了更灵活的锁定操作,相比于synchronized,它具有更多的特性。ReentrantLock可以使用lock()unlock()方法来手动加锁解锁,这意味着我们可以更加精确地控制代码的同步性。ReentrantLock还支持可重入性,即线程可以多次获得同一个锁,提供了更高的灵活性。它还支持公平锁非公平锁两种模式,可以按照先进先出的规则来选择获取锁的线程。此外,ReentrantLock还提供了一些高级功能,比如可中断锁、超时锁条件变量等。 总的来说,synchronized是Java中最基本的线程同步机制,使用简单但灵活性较低。而ReentrantLock则提供了更多的功能灵活性,但使用相对复杂一些。在实际应用中,我们可以根据具体需求来选择使用哪种线程同步机制。 ### 回答3: synchronizedReentrantLock都是Java中用于实现线程同步的工具。 synchronized是Java中最常见的同步关键字,它可以应用在方法或代码块上。当一个线程获取到对象的synchronized锁时,其他线程就无法同时访问这个对象。当该线程执行完代码块或者方法,会释放锁,其他线程才能获得锁并执行。synchronized是隐式获取释放锁的过程,非常方便,但也存在一些限制,例如无法中断一个正在等待获取锁的线程。 ReentrantLock是Java中提供的一种显示锁,可以实现与synchronized类似的功能。与synchronized不同的是,ReentrantLock提供了更灵活更强大的功能。它允许更细粒度的控制锁定的获取释放,例如通过lock()unlock()方法手动获取释放锁,也可以通过tryLock()方法尝试获取锁而不需要一直等待。此外,ReentrantLock还支持公平性,即按照线程请求的顺序获取锁,避免产生线程饥饿现象。而synchronized则不保证公平性。 另外,ReentrantLock提供了Condition接口,可以使用newCondition()方法创建多个条件变量。条件变量可以用于线程间的通信,可以让某个线程等待特定条件满足后再继续执行,或者通知其他等待线程某个条件已经满足。 总的来说,synchronized是Java中隐式锁机制的实现,使用方便但功能有限;而ReentrantLock是显示锁机制的实现,提供了更多灵活强大的功能,但使用起来需要更细致的控制。根据具体情况需要,我们可以选择合适的同步机制来保证线程安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值