java线程同步机制以及对象锁机制

来源:互联网 发布:sql 查询过期时间 编辑:程序博客网 时间:2024/06/06 00:15

一、java线程同步机制以及对象锁

1、线程同步

众所周知,在多线程编程中,多个 线程同时对同一个资源进行访问,就可能会出现这样的情况-----这几个线程根据时间片机制会争向访问该资源,特别是在访问某一静态变量

的时候,比如我们所谓的火车票卖票系统,设置一个静态变量 static  int ticketCount; 多个线程同时买票,每卖一张票  ticketCount计数器就减1,并且不能使得ticketConut小于

0(即当ticketCount为0的时候就结束卖票)。如果不使用同步机制的情况下,某个线程在执行 卖票的run方法的时候,另一个线程也可能进入,使得  ticketCount计数器小于0。所

以在这种情况下就得使用java的同步机制来做。

同步机制是由synchoronized关键字修饰的,一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个

对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中)。 取到锁后,他就开始执行同步代码(被

synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只

有一个线程在执行。

2、线程同步的方式

(1)synchoronized关键字修饰成员方法

  1. public class ThreadTest extends Thread {     
  2.      private int threadNo;     
  3.      public ThreadTest(int threadNo) {     
  4.          this.threadNo = threadNo;     
  5.      }     
  6.      public static void main(String[] args) throws Exception {     
  7.          for (int i = 1; i < 10; i++) {     
  8.             new ThreadTest(i).start();     
  9.              Thread.sleep(1);     
  10.          }     
  11.       }     
  12.        
  13.      @Override    
  14.       public synchronized void run() {     
  15.          for (int i = 1; i < 10000; i++) {     
  16.              System.out.println("No." + threadNo + ":" + i);     
  17.          }     
  18.       }     
  19.   }  

以上这个例子就是首先创建10个线程,然后分别数数从1到9999,通过同步,我们本来想看到的是一个线程从1数到9999,然后第二个开始数,但是控制台的打印的结果还

是杂乱无章的,各个线程争相数数,没有做到我们想要的同步效果。这是为什么呢??

实际上,对于成员方法加上synchronized关键字,是以这个成员方法所在的对象本身作为对象锁,在上例中,就是以ThreadTest类的一个具体对象,也就是该线程自身作

为对象锁的;一共10个线程,每个线程持有自己线程对象的那个对象锁,必然不能产生同步的效果。怎么样才能使得对这些线程进行同步呢??毫无疑问,如果能够使得这些

线程拥有共享且唯一的对象锁,这样的问题就会迎刃而解。

2、同步代码块

  1.  public class ThreadTest2 extends Thread {     
  2.   private int threadNo; 
  3.   private String lock;     
  4.   public ThreadTest2(int threadNo, String lock) {     
  5.       this.threadNo = threadNo;     
  6.       this.lock = lock;   }     
  7.  public static void main(String[] args) throws Exception {     
  8.     String lock = new String("lock");     
  9.       for (int i = 1; i < 10; i++) {       
  10.            new ThreadTest2(i, lock).start();     
  11.            Thread.sleep(1);     
  12.      }     
  13.   }       
  14. public void run() {       
  15.  synchronized (lock) {     
  16.       for (int i = 1; i < 10000; i++) {     
  17.        System.out.println("No." + threadNo + ":" + i);     
  18.     }        
  19.  }       
  20.  }     
  21.  } 

在run方法中加入一个静态代码块,并引入一个通过构造方法赋值的 私有变量 lock,这个new出来的String 变量是存放在堆内存上的,它是唯一的,各个线程共同享有该

lock变量,该lock变量就是所谓的对象锁,这样做就能做到同步的效果。

3、使用synchronized修饰静态方法

  1. public class ThreadTest3 extends Thread {     
  2.      
  3.     private int threadNo;     
  4.     private String lock;     
  5.      
  6.     public ThreadTest3(int threadNo) {     
  7.         this.threadNo = threadNo;     
  8.     }     
  9.      
  10.     public static void main(String[] args) throws Exception {     
  11.          
  12.         for (int i = 1; i < 20; i++) {     
  13.             new ThreadTest3(i).start();     
  14.             Thread.sleep(1);     
  15.         }     
  16.     }     
  17.      
  18.     public static synchronized void abc(int threadNo) {     
  19.         for (int i = 1; i < 10000; i++) {     
  20.                 
  21.                 System.out.println("No." + threadNo + ":" + i);             
  22.         }     
  23.     }     
  24.      
  25.     public void run() {     
  26.         abc(threadNo);     
  27.     }     
  28. }  

上例中,我们在run方法中,调用一个静态的abc方法,这个静态abc方法被synchronized所修饰,同样做到了同步效果。

下面我们来分析下第三种同步锁机制:我们知道静态方法是属于类的,由于在JVM中,所有被加载的类都有唯一的类对象具体到本例,就是唯一的

 ThreadTest3.class对象。不管我们创建多少个该类的实例,它的对象实例只有一个。对于同步静态方法,对象锁就是该静态方法所在的类的Class实例。

二、总结

使用synchronized修饰成员方法和静态方法,他们的主要区别就是静态方法的对象锁是类实例,且类实例是唯一的,而成员变量的对象锁是该类的实例,每个线程可以分别

拥有不同的实例对象。另,单例模式下成员方法能够起到同步的效果。


原创粉丝点击