`
huiqinbo
  • 浏览: 344486 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java多线程同步Synchronized使用并详细分析与实例 【huiqinbo转于网络并总结】

 
阅读更多
一.Synchronized基本知识:
 
在Java应用中,使用多线程进行工作的需要是越来越多,使用多线程进行工作,大大的提高了系统的工作效率,然而因此而产生的问题也是层出不穷,而且因为多线程而产生的问题跟踪是一个难题。

同步的概念:
    同步分为 方法同步 和 同步块  两种方式。

使用同步的原因
   1. 在系统中对访类要使用多线程进行访问;
   2. 在该类中有 类变量, 或者是 在类的方法中有访问 公共资源(如一个外部文件的读写)。

 

同步所锁定的内容是什么?

    

 

下例中分情况的列举各种情况下的同步效果

 

1. Synchronized 加在方法上, (方法同步)

 

public  synchronized void function1(){ ……}

public  void function2(){

         synchronized (this){……}

    ……}

这两种写法的效果是一样的,锁定的都是类实例对象。

如果有一个 类实例对象:   inst = new ClassSynInst(),

另外有两个线程: threada,threadb,都调用了inst 对象,那么,在同一时间,如果 threada调用了inst.function1(),则threadb在该时间内不能访问inst.function1() 和 inst.function2(); 因为threada把inst这个对象的锁使用了,所以无法分给其它线程使用

但是,如果threada调用 inst1.function1(), threadb调用 inst2.function1(), 则可以同时进行,因为它们调用的是不同的ClassSynInst类对象实例。

 

钦波总结:无论你将Synchronized加在方法前还是加在一个变量前,其锁定的都是一个 类对象。 每一个对象都只有一个锁与之相关联。

 

 

 

2. Synchronized 加在变量上, (同步块)

 Object a = new Object();

 Object b = new Object();

 

 

public  void function1(){

         synchronized (a){……}

    ……}

public  void function2(){

         synchronized (b){……}

    ……}

 

这种情况下,是实现代码块锁定,锁定的对象是 变量 a 或 b; (注意,a 、b 都是非static 的)

 

如果有一个 类实例对象:   inst = new ClassSynInst(),

另外有两个线程: threada,threadb,都调用了inst 对象,那么,在同一时间,如果 threada调用了inst.function1(),则threadb在该时间内可以访问inst.function2(); 但不能访问  inst.function1() 的同步块, 因为a被 threada锁定了。

 

钦波总结:你将Synchronized锁在代码块中,其锁定的是一个 类对象,而锁定的是变量a或b。 

 

 

3. Synchronized 锁定的是 类变量 ,即static 变量

 

class Test{ 
 static Object o= new Object(); 

public static synchronized void f1(){ ……} 


public static void f2(){ 
synchronized(Test.class){ ……} 


public static void f3(){ 
try { 
synchronized (Class.forName("Test")) { ……} 

catch (ClassNotFoundException ex) { 



public static void f4(){ 
synchronized(o){ ……} 

 

钦波总结:以上4个方法中实现的效果都是一样的,其锁定的对象都是类Test,而不是类实例对象 ,即在多线程中,其共享的资源是属于类的,而不是属于类对象的。

在这种情况下,如果threada 访问了这4个方法中的任何一个, 在同一时间内其它的线程都不能访问 这4个方法

 

 

4. 类的方法中访问了多线程共同的资源, 且该资源是可变的,这种情况下也是需要进行同步的

例:

class test {

    static String path = “ file path”;    

    public void  readConfiFile(){

          synchronized (path){

              // 读取该path指定的文件。

         }

 

   }

 

     public void  writeConfiFile(){

        synchronized (path){

              // 写信息到该path指定的文件。

         }

   }

 

}

 

这种情况下,必须锁定为 类变量,而不能进行锁定类实例对象,因为这是变象的一种类资源共享,而不是类实例对象资源共享。

 

 

线程,成也其,败也其,用好了可以提升性能,用不好则会使系统后患无穷。

 

PS: 进行线程同步需要很大的系统开销, 所以,在使用时,如果不是必须的,则尽量不使用同步功能。

 

 

 

二.Synchronized实用例子:

 

先看一个例子:

//模仿火车售票系统,假设有4个售票点同时售票,我们可以把四个售票点定义为四个独立线程

class Test
{
 public static void main(String[] args)
 {
  TestThread tt=new TestThread();
  new Thread(tt).start();        //每一个售票点定义为一个独立的线程,共同卖出所有的票
  new Thread(tt).start();
  new Thread(tt).start();
  new Thread(tt).start();
 }
}

class TestThread implements Runnable
{
 int ticket=10;                 //票数定义
 public void run()
 {
  while(ticket>0)
  {

     System.out.println(Thread.currentThread().getName()+":ticket "+ticket+"is saling");

     ticket--;

  }

 }

}

运行结果为:

Thread-0:ticket 10is saling
Thread-0:ticket 9is saling
Thread-0:ticket 8is saling
Thread-0:ticket 7is saling
Thread-0:ticket 6is saling
Thread-0:ticket 5is saling
Thread-0:ticket 4is saling
Thread-0:ticket 3is saling
Thread-1:ticket 3is saling
Thread-2:ticket 3is saling
Thread-3:ticket 3is saling
Thread-1:ticket 2is saling
Thread-2:ticket 1is saling

我们可以看出来票3倍卖出了4次,很明显不正确,我们仔细观察程序,可以发现问题出在

while(ticket>0)
  {
   System.out.println(Thread.currentThread().getName()+":ticket "+ticket+"is saling");

   ticket--;

  }  

的执行当中,当线程0(模拟售票点1)执行System.out.println(Thread.currentThread().getName()+":ticket "+ticket+"is saling");语句时(模拟售票点1卖出票3),这时,CPU切换到了线程1(售票点2)

而这时,售票点0卖出了票3,但是在剩余的票中没有及时将票3除去(即执行ticket--;语句),所以此时在线程1处,并不知道票3已经卖出,所以它又把票3卖出,同理,售票点2和3也重复地卖了票3,原因是我们无法确定CPU无法进行线程切换,如果我们可以把一段要执行的语句放到一个无法分开的块中,使CPU没有执行完这个块时无法去执行其他的块,我们就可以正确实现上述售票模拟了,这就是synchronized关键字的作用!!

 

我们把上述实现改动一下,并加入synchronized代码块

 

class Test
{
 public static void main(String[] args)
 {
  TestThread tt=new TestThread();
  new Thread(tt).start();
  new Thread(tt).start();
  new Thread(tt).start();
  new Thread(tt).start();
 }
}

class TestThread implements Runnable
{
 int ticket=10;
 String str=new String(" ");
 public void run()
 {
  while(true)
  { 
   synchronized(str)                    //synchronized 代码块
   {
    if(ticket>0)
    {
     System.out.println(Thread.currentThread().getName()+":ticket "+ticket--+"is saling");
    }
   }
  }
 }
};

 

运行一下,结果为:

Thread-0:ticket 10is saling
Thread-0:ticket 9is saling
Thread-0:ticket 8is saling
Thread-0:ticket 7is saling
Thread-0:ticket 6is saling
Thread-0:ticket 5is saling
Thread-0:ticket 4is saling
Thread-0:ticket 3is saling
Thread-1:ticket 2is saling
Thread-2:ticket 1is saling

 

结果正确

 

我们再改动一下我们的程序:

class Test
{
 public static void main(String[] args)
 {
  TestThread tt=new TestThread();
  new Thread(tt).start();
  new Thread(tt).start();
  new Thread(tt).start();
  new Thread(tt).start();
 }
}

class TestThread implements Runnable
{
 int ticket=10;
 String str=new String(" ");
 public void run()
 {
  while(true)
  {
   sale();
  }
 }
 public synchronized void sale()      //synchronized方法
 {
  if(ticket > 0)
   System.out.println(Thread.currentThread().getName()+":ticket "+ticket--+"is saling");

 }
};

运行结果为:

Thread-0:ticket 10is saling
Thread-0:ticket 9is saling
Thread-0:ticket 8is saling
Thread-0:ticket 7is saling
Thread-0:ticket 6is saling
Thread-0:ticket 5is saling
Thread-0:ticket 4is saling
Thread-0:ticket 3is saling
Thread-1:ticket 2is saling
Thread-2:ticket 1is saling

结果也正确!

 

由此我们给出synchronized关键字在多线程中的作用:

两种用法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如上述例子所述。
synchronized 方法控制对类成员变量的访问:每个类实例对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例对象,其所有声明为 synchronized 的成员方法中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对象对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用(如上述例子中的sale()方法,在run()方法中来调用sale()方法)来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类,上述例子中我们定义了一个String类的对象)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

对synchronized(this)的一些理解

 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.

 

更多交流、更多了解:

QQ:444084929  个人主页:http://www.huiqinbo.com

分享到:
评论

相关推荐

    Java多线程同步具体实例.doc

    这个实例展示了如何在Java中使用多线程同步来避免竞态条件,确保数据的一致性。在实际开发中,我们应当根据具体情况选择适合的同步策略,如使用同步方法、同步块,或者其他的并发控制工具,如`ReentrantLock`等,以...

    Java多线程同步具体实例讲解 .doc

    总结起来,Java多线程同步是通过`synchronized`关键字实现的,它可以应用于方法或代码块,保证同一时刻只有一个线程能够执行特定的代码。通过合理使用同步机制,开发者可以有效地管理并发程序中的资源访问,避免数据...

    Java多线程同步.pdf

    Java多线程同步是指在Java语言中,如何使用synchronized关键字和其他同步机制来确保多线程程序的正确执行。在Java语言中,synchronized关键字用于对方法或者代码块进行同步,但是仅仅使用synchronized关键字还不能...

    java 多线程并发实例

    在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...

    Java多线程同步机制研究分析.pdf

    Java多线程同步机制研究分析 Java多线程同步机制是Java编程语言中的一种机制,它允许多个线程同时执行,提高了系统资源的利用率和安全性。但是,多线程中最重要的问题是线程的同步和共享资源的访问保护。本文通过对...

    java 多线程同步方法的实例

    本文将深入探讨Java中的多线程同步方法,并通过实例来阐述其工作原理。 首先,我们要理解线程同步的基本概念。在多线程环境中,当多个线程试图访问同一资源时,如果没有适当的同步控制,可能会导致不可预测的结果。...

    Java多线程同步机制的应用分析.pdf

    Java多线程同步机制的应用分析是指在Java语言中,如何使用同步机制来保护临界区,以避免多线程之间的冲突和错误。该机制通过管程机制和同步语法来保护临界区,使得多线程可以安全、高效地运行。 在多线程编程中,...

    java 多线程synchronized互斥锁demo

    通常,这个例子会创建一个`Long`实例,并在多线程环境中使用`synchronized`关键字保护对这个实例的修改操作,确保在任何时候只有一个线程能够进行修改。 总结来说,Java中的`synchronized`关键字是实现线程同步的...

    Java多线程知识点总结

    Java提供了多种机制来保证线程安全,比如使用synchronized关键字来同步方法或代码块,实现线程之间的同步。当一个线程试图进入一个已经被另一个线程持有的同步代码块时,它将进入阻塞状态,直到同步代码块的执行线程...

    java多线程的讲解和实战

    本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者以及希望深入理解多线程的开发者。 1. **线程的基本概念**:线程是程序执行的最小单位,一个进程中可以有多个线程同时运行。Java...

    JAVA多线程的实例

    总的来说,Java多线程涉及到线程创建、同步、通信、管理等多个方面,理解并熟练掌握这些知识点对于编写高效、稳定的并发程序至关重要。在实际开发中,我们需要根据具体需求选择合适的方法来处理多线程问题。

    java多线程经典案例

    通过分析并实践`threadTest`案例,我们可以深入理解Java多线程的原理和使用技巧,为编写高效并发程序打下坚实基础。同时,也要注意多线程编程中的死锁、活锁和饥饿等问题,合理设计线程间的交互,避免出现不可预期的...

    java多线程编程实例_Source

    在本实例源码中,包含17个章节和上百个实例,旨在深入讲解Java多线程的核心概念和实际应用。 一、线程基础知识 在Java中,线程是程序的执行流,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈和一部分堆内存...

    java多线程中synchronized关键字的用法

    java多线程中synchronized关键字的用法 解压密码 www.jiangyea.com

    java的线程同步机制synchronized关键字的理解_.docx

    Java 线程同步机制中 synchronized 关键字的理解 Java 的线程同步机制是为了解决多个线程共享同一片存储空间所带来的访问冲突问题。其中,synchronized 关键字是 Java 语言中解决这种冲突的重要机制。 ...

    基于Java多线程同步的安全性研究.pdf

    最后,文章总结了基于Java多线程同步的安全性研究的重要性和必要性,并提出了解决Java多线程同步机制中安全性问题的方法。本文为Java多线程同步机制的研究和实现提供了有价值的参考。 知识点: 1.Java多线程同步...

    Java多线程编程实例

    总的来说,“Java多线程编程实例”这本书涵盖了Java多线程编程的各个方面,从基础概念到高级用法,包括线程创建、同步机制、线程池、线程通信以及并发工具类的使用,都是现代Java开发者必备的知识。虽然年代久远,但...

    java 多线程同步

    Java多线程同步是Java编程中关键的并发概念,它涉及到如何在多个线程访问共享资源时保持数据的一致性和完整性。`java.util.concurrent`包是Java提供的一个强大的并发工具库,它为开发者提供了多种线程安全的工具,...

Global site tag (gtag.js) - Google Analytics