Java中线程同步的方法

   本文由广州疯狂软件java培训为你整理:

  用什么关键字修饰同步方法?用synchronized关键字修饰同步方法

  同步有几种实现方法,都是什么?分别是synchronized,wait与notify

  wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

  sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

  notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

  Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

  实现同步的方式

  同步是多线程中的重要概念。同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果。同步的实现方式有两种,同步方法和同步块,这两种方式都要用到synchronized关键字。

  给一个方法增加synchronized修饰符之后就可以使它成为同步方法,这个方法可以是静态方法和非静态方法,但是不能是抽象类的抽象方法,也不能是接口中的接口方法。下面代码是一个同步方法的示例:

  publicsynchronizedvoid aMethod() {

  // do something

  }

  publicstatic synchronizedvoid anotherMethod() {

  // do something

  }

  线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

  同步块是通过锁定一个指定的对象,来对同步块中包含的代码进行同步;而同步方法是对这个方法块里的代码进行同步,而这种情况下锁定的对象就是同步方法所属的主体对象自身。如果这个方法是静态同步方法呢?那么线程锁定的就不是这个类的对象了,也不是这个类自身,而是这个类对应的java.lang.Class类型的对象。同步方法和同步块之间的相互制约只限于同一个对象之间,所以静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例(对象)没有关系。

  如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块。

  synchronized关键字用于保护共享数据。请大家注意“共享数据”,你一定要分清哪些数据是共享数据,请看下面的例子:

  public class ThreadTest implements Runnable{

  public synchronized void run(){

  for(int i=0;i<10;i++) {

  System.out.print(" " + i);

  }

  }

  public static void main(String[] args) {

  Runnable r1 = new ThreadTest(); //也可写成ThreadTest r1 = new ThreadTest();

  Runnable r2 = new ThreadTest();

  Thread t1 = new Thread(r1);

  Thread t2 = new Thread(r2);

  t1.start();

  t2.start();

  }}

  在这个程序中,run()虽然被加上了synchronized关键字,但保护的不是共享数据。因为这个程序中的t1,t2是两个对象(r1,r2)的线程。而不同的对象的数据是不同的,r1,r2有各自的run()方法,所以输出结果无法预知。

  synchronized的目的是使同一个对象的多个线程,在某个时刻只有其中的一个线程可以访问这个对象的synchronized数据。每个对象都有一个“锁标志”,当这个对象的一个线程访问这个对象的某个synchronized数据时,这个对象的所有被synchronized修饰的数据将被上锁(因为“锁标志”被当前线程拿走了),只有当前线程访问完它要访问的synchronized数据时,当前线程才会释放“锁标志”,这样同一个对象的其它线程才有机会访问synchronized数据。

  示例3:

  public class ThreadTest implements Runnable{

  public synchronized void run(){

  for(int i=0;i<10;i++){

  System.out.print(" " + i);

  }

  }

  public static void main(String[] args){

  Runnable r = new ThreadTest();

  Thread t1 = new Thread(r);

  Thread t2 = new Thread(r);

  t1.start();

  t2.start();

  }}

  如果你运行1000次这个程序,它的输出结果也一定每次都是:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。因为这里的synchronized保护的是共享数据。t1,t2是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run()方法时,由于run()受synchronized保护,所以同一个对象的其他线程(t2)无法访问synchronized方法(run方法)。只有当t1执行完后t2才有机会执行。

  示例4:

  public class ThreadTest implements Runnable{

  public void run(){

  synchronized(this){

  for(int i=0;i<10;i++){

  System.out.print(" " + i);

  }} }

  public static void main(String[] args){

  Runnable r = new ThreadTest();

  Thread t1 = new Thread(r);

  Thread t2 = new Thread(r);

  t1.start();

  t2.start();

  }}

  这个程序与示例3的运行结果一样。在可能的情况下,应该把保护范围缩到最小,可以用示例4的形式,this代表“这个对象”。没有必要把整个run()保护起来,run()中的代码只有一个for循环,所以只要保护for循环就可以了。

  示例5:

  public class ThreadTest implements Runnable{

  public void run(){

  for(int k=0;k<5;k++){

  System.out.println(Thread.currentThread().getName()+ " : for loop : " + k);

  }

  synchronized(this){

  for(int k=0;k<5;k++) {

  System.out.println(Thread.currentThread().getName()+ " : synchronized for loop : " + k);

  }} }

  public static void main(String[] args){

  Runnable r = new ThreadTest();

  Thread t1 = new Thread(r,"t1_name");

  Thread t2 = new Thread(r,"t2_name");

  t1.start();

  t2.start();

  } }

  学IT技术就学Java技术,疯狂软件Java培训机构,打造国内高级Java人才。

  疯狂软件Java培训优势

  1. Java是世界第一大编程语言,市场占有率达20%。

  2.广州最专业Java培训机构,多年教学经验。

  3.学员技术牛就业率最好,就业薪资都在5000以上。

  4.疯狂软件Java培训课程以项目实战带动教学,上课打破传统课堂模式,以实训项目贯穿教学,带领学生一起做企业真实项目。

  疯狂Java培训专注软件开发培训,提升学员就业能力,重点提升实践动手能力。高薪从IT名企请来项目经理为学员亲自授课,对学员进行实战教学,在疯狂java,通过大量全真经典企业级项目进行集中培训,学员通过数月培训都可获得1-2年的工作经验,进而在同类的求职者中脱颖而出。

原文地址:https://www.cnblogs.com/gojava/p/3573252.html