您的位置:首页 > 编程语言 > Java开发

Java线程:同步

2015-12-21 21:59 113 查看
[b]一 同步的概念[/b]

  线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

  例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。

  [b]MyRunnable.java[/b]

package Thread;

public class BankTest {
public static void main(String[] args){
User u=new User("小二",100);
MyThread t1=new MyThread("线程1",u,20);
MyThread t2=new MyThread("线程2",u,-60);
MyThread t3=new MyThread("线程3",u,-80);
MyThread t4=new MyThread("线程4",u,-30);
MyThread t5=new MyThread("线程5",u,32);
MyThread t6=new MyThread("线程6",u,21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread{
private User u;
private int y=0;
MyThread(String name,User u,int y){
super(name);
this.u=u;
this.y=y;
}
public void run(){
u.oper(y);
}
}
class User{
private String code;
private int cash;
User(String code,int cash){
this.code=code;
this.cash=cash;
}
public String getCode(){
return code;
}
/**
* 业务方法
* @param x 添加x万元
* */
public synchronized void oper(int x){
try{
Thread.sleep(10L);
this.cash+=x;
System.out.println(Thread.currentThread().getName()+"运行结果,增加"+x+",当前余额为:"+cash);
Thread.sleep(10L);
}
catch(InterruptedException e){
e.printStackTrace();
}
}
public String toString(){
return "User{" + "code=" + code + ",cash=" + cash +'}';
}
}


View Code
结果为:

线程1运行结果,增加20,当前余额为:120
线程2运行结果,增加-60,当前余额为:60
线程6运行结果,增加21,当前余额为:81
线程5运行结果,增加32,当前余额为:113
线程4运行结果,增加-30,当前余额为:83
线程3运行结果,增加-80,当前余额为:3


但是如果把同步关键字synchronized去掉,结果为:

线程2运行结果,增加-60,当前余额为:60
线程6运行结果,增加21,当前余额为:1
线程4运行结果,增加-30,当前余额为:3
线程5运行结果,增加32,当前余额为:3
线程1运行结果,增加20,当前余额为:60
线程3运行结果,增加-80,当前余额为:-20


显然是错误的,多个线程并发访问了竞争资源u,并对u的属性做了修改。可见同步的重要性。

[b]2、同步块[/b]

  有时候,同步块比同步方法有更好的效果。在上个例子的基础上对oper方法做了改变,由同步方法改为同步块模式。

   BankTest.java

public void oper(int x){
try{
Thread.sleep(10L);
synchronized(this){
this.cash+=x;
System.out.println(Thread.currentThread().getName()+"运行结果,增加"+x+",当前余额为:"+cash);
}
Thread.sleep(10L);
}
catch(InterruptedException e){
e.printStackTrace();
}
}


结果和上例是一样的。具体的变化思想为:

1 public syncronized int getX(){
2    return x++;  //同步方法
3 }
4 与
5 public int getX(){
6   synchronized(this){
7         return x;//非同步方法
8     }
9 }


注意:

  在使用synchronized时,应该避免在synchronized方法或synchronized块中使用sleep或yield方法。因为synchronized程序块占用着对象锁,你休息那么其他线程只能等待。这样效率不高。同样,在同步程序块内调用yield方法让出CPU资源也没有意义,因为你占用着锁,其他资源无法访问。

[b]二、静态方法同步[/b]

  要同步静态方法,需要一个用整个类对象的锁,这个对象就是这个类(xxx.class),如:

1 public static synchronized int setName(String name){
2   xxx.name=name;
3 }
4 等价于
5 public static int setName(){
6   synchronized(xxx.class){
7       xxx.name=name;
8   }
9 }


[b]三、何时需要同步[/b]

  1、多个线程同时访问互斥(可交换)数据时,应该同步保护数据,确保两个线程不会同时更改它。

  2、非静态字段中可更改的数据,通常用非静态方法访问。

  3、静态字段中可更改的数据,用静态方法访问。

[b]四、总结[/b]

  1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。

  2、线程同步方法是通过锁来实现,每个对象都仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程无法再访问该对象的其他同步方法。

  3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁不干预。一个线程获得锁,当在一个同步方法中访问另外对象的同步方法时,会获取两个对象的锁。

  4、对于同步,要时刻清醒哪个对象上同步,这是关键。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: