并发基础之一、多线程synchronized

关于synchronized

public class Demo{
	private int count = 10;
	privare Object object = new Object();
	public void test(){
		synchronized(obejct){
			count--;
			System.out.println(Thread.currentThread().getName()+" count = "+count);
		}
	}
	
	//简便的写法一、
	public void test(){
		synchronized(this){
			count--;
			System.out.println(Thread.currentThread().getName()+" count = "+count);
		}
	}
	
	//简便的写法二、
	public synchronized void test(){
		count--;
		System.out.println(Thread.currentThread().getName()+" count = "+count);
	}
	//在这里的排序也是有讲究的,synchronized虽然在一、二这里起到的作用相同。
	//但是synchronied如果只需要一部分代码需要上锁,那么就不要锁整个代码块,锁一小部分代码块就行了
	//这样的好处在于写法一与写法二的性能上,一的性能会好与二的性能,CPU征用的时间减少了,提高了效率。
}

synchronized 就是所说的锁,但是要清楚的是,它不是锁方法,不是代码块,而是分两种情况,一种是对象类的实例,一种是类的字节码。
比如:当同时有两个线程执行这个方法的时候,当第一个正在使用还未结束时,第二个线程就无法继续使用。一定程度上保证了线程的稳定和安全,但是这并不代表使用synchronized就代表着线程安全。

public class Demo{
	public static void test2(){
		//能否将Demo.class换成this
		synchronized (Demo.class){
			count--;
		}
	}
}

答案是:不能!静态(static)方法不能使用this,因为静态方法可以直接通过调用class.method(类名.方法名)来使用,那么就没有这个实例了,没有就锁不到东西了。所以只能通过Demo.class来锁了

相关面试题:
1.同步方法和非同步方法是否可以同时调用?

答案:可以同时调用,因为它们并非同一个方法自然可以同时调用了。

2.同步方法调用同步方法能否得到锁?

答案:可以。
	  1.因为它们是用一个锁的,当一个方法里面,还有一个方法也需要这个锁的时候,那么这个方法也是可以得到锁的。
	  2.这证明synchronized支持 重入锁 的
	  3.顺便一提,继承关系下的锁也是共享的,也是可以重入锁的。

关于脏读问题:

 - 主要表现在读与写方面。例如:一个银行为例子,用户存钱为写,显示余额为读。
 - 为了保证用户能够成功存钱,一般都会在写上加上synchronized来保证成功率。
 - 但是显示余额上,它却没办法马上读取到刚刚存进去的那部分钱。
 - 所以也需要在读的方法上面也加上synchronized来保证数据的读取。

volatile:

 - 是synchronized的一个轻量级关键字,它不能保证量子性,却能保证可见性。
 - 在JAVA的主内存里(JAVA只有一个主内存),当一个变量在线程中被修改的时候,JAVA的内存模型JMM是不会刷新的,依旧会以变量原本的值进行运行。
 - 倘若你在变量的修饰中加了volatile,它的可见性,会通知JMM里所有的进程,这个变量的值变化了。
 - 其实不加的话也可以,以现在的科技,可以完全不用加。
 - 因为现在的CPU都很高级,性能也很好,现在的CPU有一个缓存协议,它能够保证每个CPU的缓存都能及时刷新过来。
 - 缺点:volatile只保证可见性,却没办法保证原子性,它不会管你读取前是什么,只会管显示。倘若10个线程同时i++一百次,按常理i会是1000,但是只有volatile的话,只会少于1000。
 - 解决方法:在i++一百次的方法上加synchronized来修饰,倘若不想用synchronized的话,可以用atomic的类来解决。整型用AtomicInteger.incrementAndGet()来解决足以。
 - 但是即便如此,多个atomic类连续调用也不能构成原子性。

死锁:

public class Demo{
	String s1="Hello";
	String s2="Hello";
	public void test1(){
		synchronized(s1){
			.....
		}
	}
	public void test2(){
		synchronized(s2){
			.....
		}
	}
	public static void main(String[] args){
		Demo dm = new Demo();
		new Thread(dm::test1,"test1").start();
		new Thread(dm::test2,"test2").start();
	}
}

死锁是多线程中一个诡异的现象,明明用来当锁的对象并不一样,按照道理来说应该是互不阻塞的。但是运行的时候却会发生阻塞的现象。原因就在于s1和s2是常量。s1和s2是常量,并且都是“Hello”,所以它们在常量池里的地址其实是一样的,所以我们要避免使用常量作为锁。这也是为什么一般都是用对象来当锁的原因。

 - 死锁原因
 - 当test1运行的时候,由于地址一样。
 - 原本只用s1的test1在过程中会用到s2,用s2为锁test2也会在过程中运用到s1,
 - 造成了各方都在等待另一方释放钥匙。最后的结果就是发生了阻塞。	

wait():等待线程。
notify():唤醒线程,能够唤醒wait()等待线程。
但是要注意的是:

 - wait()和notify()必须在同一个方法里面。
 - wait()等待会释放锁,但是notify()不会。
 - 所以即便notify()唤醒了wait()所在的线程,wait()所在的线程也没办法马上运行。
 - 因为它的锁正在nofity()那里,需要等它结束后才能用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值