JAVA 线程同步 synchronized

来源:互联网 发布:游族网络最新消息 编辑:程序博客网 时间:2024/05/19 23:57

先举个例子说明一下不采用线程同步带来的后果,假设现在我手上有20张票,雇佣2个人帮我卖票(2个人共享20张票),假设就剩最后一张了,那么如果一个人先看自己手上还有票,就卖给别人了,如果判断和卖个别人中间所花费时间过长,线程切给另一个人,此时票数还是2张,他认为也可以卖,就卖出了两张票。

public class testdouble implements  Runnable{  private int total=20;//一共20张票  public void run() {   while(true){if (total>0){//如果还有票try {Thread.sleep(100);//让当前操作延迟一段时间(假设某段耗时较长的操作)} catch (InterruptedException e) {e.printStackTrace();}total--;System.out.println("现在还有:"+total+"张票");}else break;  } }  public static void main(String[] args) {testdouble t=new testdouble();Thread t1=new Thread(t);Thread t2=new Thread(t);t1.start();t2.start();}}

现在还有:18张票现在还有:18张票现在还有:17张票现在还有:16张票现在还有:15张票现在还有:14张票现在还有:13张票现在还有:12张票现在还有:11张票现在还有:10张票现在还有:9张票现在还有:8张票现在还有:7张票现在还有:6张票现在还有:5张票现在还有:4张票现在还有:3张票现在还有:2张票现在还有:1张票现在还有:0张票现在还有:-1张票

为了避免这种情况的发生,我们使用synchronized进行修饰。

当一个线程拿到 synchronized修饰的对象时,其他也需要拿到这个对象才能运行的线程不能执行了。

(线程——对象)

public class testdouble2 implements Runnable{  private int total=20;//总共有20张票  public synchronized void run(){  while(true){ if (total>0){  try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}  total--;  System.out.println("现在还有:"+total+"张票");  }  else{ break;   }  }  }  public static void main(String[] args) {testdouble2 t=new testdouble2();Thread t1=new Thread(t);Thread t2=new Thread(t);t1.start();t2.start();}}

现在还有:19张票现在还有:18张票现在还有:17张票现在还有:16张票现在还有:15张票现在还有:14张票现在还有:13张票现在还有:12张票现在还有:11张票现在还有:10张票现在还有:9张票现在还有:8张票现在还有:7张票现在还有:6张票现在还有:5张票现在还有:4张票现在还有:3张票现在还有:2张票现在还有:1张票现在还有:0张票


或者采用以下方法:

public class testdouble2 implements Runnable{  private int total=20;//总共有20张票  public   void run(){  String a="now ok";  while(true){  synchronized (a) {//添加线程同步   if (total>0){  try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}  total--;  System.out.println("现在还有:"+total+"张票");  }  else{ break;   }  }  }  }  public static void main(String[] args) {testdouble2 t=new testdouble2();Thread t1=new Thread(t);Thread t2=new Thread(t);t1.start();t2.start();}}

比较奇妙的是以下方法:

public class testdouble2 implements Runnable{  private int total=20;//总共有20张票  public  synchronized void run(){  String a="now ok";  while(true){ if (total>0){  try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}  total--;  System.out.println("现在还有:"+total+"张票");  }  else{ break;   }  }  }  public static void main(String[] args) {Thread t1=new Thread(new testdouble2());//注意Thread t2=new Thread(new testdouble2());t1.start();t2.start();}}
此种方法无法防止,理由是synchronized是拿到所修饰的对象,而这两个线程是分属两个不同的对象的(各自new 出来的),所以无法避免,这也就是说extends无法使用synchronized关键字实现线程共享(因为extends肯定是各自new出两个新对象)。

事实上,即使是i++也会导致不同步:

int i=0;public int getNext(){return i++;}

因为jvm首先在jvm堆给i分配一个内存存储场所,线程启动后会自动分配一片操作数栈,现将这个值读取到栈中操作再写回堆中,所以需要间隔一段时间,这段时间如果切换线程就会导致不同步。

总结:synchronized关键字实现线程共享的方法是获得类所对应的的对象,在自己使用时,避免其他线程使用该对象。

可以使用在方法上,也可以使用在对象上。


0 0