[学习笔记]java并发编程(synchronized详解)

来源:互联网 发布:c语言整形强制转换实数 编辑:程序博客网 时间:2024/06/13 10:22
平时做项目的时候,或多说少的涉及到多线程的环境,那么如何在多线程中保证线程的安全,这是我们必须要考虑的,尤其是,银行之间的转账和取钱操作之间,必须要保证,每个时刻,只能有一个线程来操纵方法。而在java中为我们提供了synchronized关键字。
例如我们有一个大房子,里面有很多的房间,这些房间有上锁的(synchronized修饰的方法),
和普通的房间(普通的方法),然而房子的钥匙就放置在大门口,因此当第一个人来的时候,就要先去拿钥匙,如果有钥匙的话,就可以拿着钥匙进入房间去,用完后需要归还,哪怕还需要进入,也需要把钥匙归还后再去取钥匙。当然如果有很多人都在等钥匙的话,那么钥匙归谁呢,这就看JVM如何来分配资源调配了。不确定的。
这里需要注意的是,为了保证房子里面上锁的房间的安全,正如上图所示,必须要保证上锁的房间的钥匙是唯一的。

在实际中,锁可以放置在方法上,也可以作为一个单独语句块来上锁,换句话说我们也可以把一个房间里面的一块区域来上锁。(必须要保证锁是唯一的)


下面来看几个例子,通过例子来深入的理解一下synchronized关键字。

[java] view plain copy print?
<span style="font-family:Comic Sans MS;font-size:18px;">package com.test; 



public class TraditionThread { 
public static void main(String[] args) { 
final Outputer outputer = new Outputer(); 
new Thread(new Runnable(){ 
@Override 
public void run() { 
while(true){ 
try { 
Thread.sleep(10); 
} catch (InterruptedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

outputer.output("哈哈哈"); 



}).start(); 

new Thread(new Runnable(){ 
@Override 
public void run() { 
while(true){ 
try { 
Thread.sleep(10); 
} catch (InterruptedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

outputer.output2("大大大大阿达"); 



}).start(); 



static class Outputer { 

public void output(String name) { 

//如果有很多需要锁住的方法的话,则也必须保证只有一把钥匙,这个意思就是锁住的方法的钥匙都是一样的 
//锁一定要是同一个对象才可以 
//这样的话就保证了只有一把钥匙 
String xxxString=""; 
int len = name.length(); 
//钥匙不一样,所以都可以打开这扇门,如果要保持线程唯一的话,只能有一把钥匙 
synchronized (xxxString) { 
for (int i = 0; i < len; i++) { 
System.out.print(name.charAt(i)); 

System.out.println(); 



//而同步方法则是this对象 
public synchronized void output2(String name) { 

int len = name.length(); 
for (int i = 0; i < len; i++) { 
System.out.print(name.charAt(i)); 

System.out.println(); 





</span> 

可以看到在main方法中,同一个对象分别在两个线程中调用两个加锁的方法,这时候为了保证线程中的安全,也就是上面提到的,我在进入一个加锁房间的时候,别人是不能进入的,为了保证能够实现这种效果,因此每个房间的锁都是一样的,可以看上面的程序第一个房间的锁的钥匙是一个xxxString类型的对象,而第二个房间的锁的钥匙是当前的类,也就是this,因此房间的钥匙是不一样的,所以保证不了线程的安全,需要要修改的话,只需要将第一个房间的钥匙修改为this,即synchronized (this) {}。
变动一
如果我们这时候把main方法修改一下的话,让两个对象分别在两个线程中调用的话,会是怎样的效果呢?大家可想而知,这样的话,相当于我有两套同样的房子,那么这两个对象都可以拿到各自的钥匙,都可以进入,因此还是保证不了线程的安全
[java] view plain copy print?
<span style="font-family:Comic Sans MS;font-size:18px;">public class TraditionThread { 
public static void main(String[] args) { 
final Outputer outputer = new Outputer(); 
new Thread(new Runnable(){ 
@Override 
public void run() { 
while(true){ 
try { 
Thread.sleep(10); 
} catch (InterruptedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

outputer.output("哈哈哈"); 



}).start(); 

new Thread(new Runnable(){ 
@Override 
public void run() { 
while(true){ 
try { 
Thread.sleep(10); 
} catch (InterruptedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

//两个对象,更加保证不了线程的安全 
new Outputer().output2("大大大大阿达"); 



}).start(); 

}</span> 


变动二
此时,我们再添加另外一间加锁的房间,所以修改后的代码如下
[java] view plain copy print?
<span style="font-family:Comic Sans MS;font-size:18px;">//加锁的房子 
static class Outputer { 

public void output(String name) { 

//如果有很多需要锁住的方法的话,则也必须保证只有一把钥匙,这个意思就是锁住的方法的钥匙都是一样的 
//锁一定要是同一个对象才可以 
//这样的话就保证了只有一把钥匙 
String xxxString=""; 
int len = name.length(); 
//钥匙不一样,所以都可以打开这扇门,如果要保持线程唯一的话,只能有一把钥匙 
synchronized (this) { 
for (int i = 0; i < len; i++) { 
System.out.print(name.charAt(i)); 

System.out.println(); 



//而同步方法则是this对象 
//加锁的房间 
public synchronized void output2(String name) { 

int len = name.length(); 
for (int i = 0; i < len; i++) { 
System.out.print(name.charAt(i)); 

System.out.println(); 


//加锁的房间 
public static synchronized void output3(String name){ 
int len = name.length(); 
for(int i=0;i<len;i++){ 
System.out.print(name.charAt(i)); 

System.out.println(); 

}</span> 

下面我们再来分析一下,新加入的房子的锁的钥匙,因为是静态类,在内存中可以不用实例化对象,就可以调用,因此读取的是字节码,因此这间房子的钥匙是字节码,也就是Outputer.class,所以如果上述还是那么来做的话,还是保证不了线程的唯一。

小结
通过上述几个例子的分析,我们可以得到,如果使用synchronized的话,就必须保证钥匙的唯一,正因为钥匙只有一把,所以在执行某个加锁的方法时,才可以不被打扰。
0 0
原创粉丝点击