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

(转)Java线程安全精解

阅读更多
一直不敢写点什么,是因为战战兢兢,生怕写的不好甚至写错了会误人子弟。随笔可以随便写一下,不用太过计较,可是技术从来都要不得半点马虎,差之毫厘,谬以千里啊!但敝帚自珍又不是我的风格,虽然文笔不好,也要勉为其难了。废话少说,进入正题。



       从我开始接触Java的多线程起就总是觉得书上讲的不是那么清楚。不是说读完了不会写,而是对写出来的多线程代码懵懵懂懂,不知道每一句会有什么影响,心里感觉忐忑。后来仔细研读Java语言规范后,才慢慢搞明白一些细节。我主要想说的,也就是这些经验吧。



       首先要搞清楚的是线程的共享资源,共享资源是多线程中每个线程都要访问的类变量或实例变量,共享资源可以是单个类变量或实例变量,也可以是一组类变量或实例变量。多线程程序可以有多个共享资源。下面描述他们之间的一对多关系(*表示多):

     

                     多线程程序(1)----共享资源(*)----类变量或实例变量(1…*)



只有类变量和实例变量可以成为共享资源,细分如下:

1.       实现线程的类(继承Thread类、实现Runnable接口的类)的类变量、实例变量。

2.       实现线程的类的类变量、实例变量的类变量、实例变量,可以不规范的写为:TreadClass.ClassOrInstanceVar[.ClassOrInstanceVar]*,[]*的内容表示无限可重复。

3.       不是实现线程的类,但其对象可能是线程的类变量或实例变量。如Servlet、EJB。这些类的类变量和实例变量,不规范的写为:ServletOrEJB.ClassOrInstanceVar[.ClassOrInstanceVar]*。

4.       特别注意:局部变量、做为参数传递的非类变量、非实例变量不是共享资源。



那么什么是线程安全呢?关于这个问题我在网上百度了一下(没办法,有时候GOOGLE用不了),发现不少人在问这个问题,也有不少错误的理解。所以我给出一个较容易理解的解释:在线程中使用共享资源时,能够保证共享资源在任何时候都是原子的、一致的,这样的线程就是线程安全的线程。还不太理解?没有关系,慢慢解释。



首先来介绍一下共享资源的类型(这是我自己分类的,为了后文好解释),共享资源从其类型可以分为三类(下文讲到变量一律指类变量或实例变量,不再特别指出):

1.       独立的基本类型共享资源,如一个简单的int变量,例:

public class Cls1 {

       private int a;

       public int getA(){return a;}

       public void setA(int a){this.a = a;}

}

可以看到a没有任何依赖。

public class Cls2{

       private int a;

       private int b;

       private int c;

       // 没有对a的访问方法,a在Cls外不可见。

}

假设上面类中b、c都不依赖a,则a是这种类型。



2.       相互依赖的基本类型共享资源,一个类中的几个基本类型变量互相依赖,但从对象设计的角度又不能单独把这几个变量设计成一个类。

假设上例Cls2中的b、c互相依赖,则属此种情况。

3.       64位的基本类型变量。这个比较特殊,因为某些机器上64变量会分成两个32位的操作,所以和1不一样。如double、long类型。

4.       类类型的共享资源。如下例中的obj:

public class Cls3{

       private SomeObj obj;

}

public class SomeObj{

       private int a;

       private int b;

}



       其次来看看什么是原子性、一致性。其实在这里我借用了事务ACID属性的A和C,熟悉的朋友就不用我废话了。所谓原子性,是指一个共享资源的所有属性在任何时刻都是一起变化、密不可分的;所谓一致性,是指一个共享资源的所有属性在变化之后一定会达到一个一致的状态。



       最后根据上述四种共享资源类型,来看看如何做到线程安全。



1.       不用做什么,只一个独立的变量,任何时候它都是原子、一致的。

2.       使用synchronized关键字,保证几个变量被一起修改、一起读取。

3.       使用volatile关键字,然后就和1一样了。

4.       和2一样处理。



当对访问共享资源的方法不同时使用synchronized关键字时,是什么样一种情况呢?这是需要特别注意的,这样不能保证线程安全!看看下面例子的运行结果就知道了(自己运行啊,我不贴结果了):

/**

* $Author: $

* $Date: $

* $Revision: $

* $History: $

*

* Created by feelyou, at time 22:31:53, 2005-11-16.

*/



public class TestThread extends Thread {



  private int a = 0;

  private int b = 0;



  public static void main(String[] args) {

    TestThread test = new TestThread();

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

      Thread thread = new Thread(test, "thread-" + i);

      thread.start();

    }

  }



  public synchronized void doWrite() {

    a++;

    try {

      sleep((int)(Math.random()*100));

    }

    catch (InterruptedException e) {

    }

    b++;

    try {

      sleep((int)(Math.random()*100));

    }

    catch (InterruptedException e) {

    }

  }



  public void print() {

    System.out.println("" + Thread.currentThread().getName() + ":a:" + a);

    System.out.println("" + Thread.currentThread().getName() + ":b:" + b);

  }



  public void run() {

    super.run();    //To change body of overridden methods use File | Settings | File Templates.

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

      doWrite();

      print();

    }

  }



  public synchronized void start() {

    super.start();    //To change body of overridden methods use File | Settings | File Templates.

  }

}



ThreadLocal?ThreadLocal对于线程安全还是很有用的,如果资源不是共享的,那么应该使用ThreadLocal,但如果确实需要在线程间共享资源,ThreadLocal就没有用了!



最后,来一个完整的线程安全的例子:

/**

* $Author: $

* $Date: $

* $Revision: $

* $History: $

*

* Created by feelyou, at time 22:31:53, 2005-11-16.

*/



public class TestThread extends Thread {



  private int a = 0; //独立的共享资源

  private int b = 0; //b、c互相依赖

  private int c = 0;

  private volatile long d = 0L; //64位

//  private SomeObj obj = new SomeObj(); //对象类型,大家自己写吧,我就不写了。



  public static void main(String[] args) {

    TestThread test = new TestThread();

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

      Thread thread = new Thread(test, "thread-" + i);

      thread.start();

    }

  }



  public synchronized void doWrite() {

    b++;

    try {

      sleep((int)(Math.random()*100));

    }

    catch (InterruptedException e) {

    }

    c++;

    try {

      sleep((int)(Math.random()*100));

    }

    catch (InterruptedException e) {

    }

  }



  public synchronized void print() {

    System.out.println("" + Thread.currentThread().getName() + ":b:" + b);

    System.out.println("" + Thread.currentThread().getName() + ":c:" + c);

  }



  private void setA(int a) {

      this.a = a;

  }



  private int getA() {

      return a;

  }



  public long getD() {

      return d;

  }



  public void setD(long d) {

      this.d = d;

  }



  public void run() {

    super.run();    //To change body of overridden methods use File | Settings | File Templates.

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

      doWrite();

      print();

      setA(i);

      System.out.println(getA());

      setD(18456187413L * i);

      System.out.println(getD());

    }

  }



  public synchronized void start() {

    super.start();    //To change body of overridden methods use File | Settings | File Templates.

  }

}

写的比较匆忙,如果有错误,还请大家指正,谢谢!
分享到:
评论

相关推荐

    Java多线程编程精解

    - **优先级**:Java线程有10个优先级,从`MIN_PRIORITY`(1)到`MAX_PRIORITY`(10),默认优先级是`NORM_PRIORITY`(5)。优先级高的线程更可能被操作系统优先调度,但不保证。 6. **守护线程与用户线程** - **守护...

    Java网络编程精解(孙卫琴)电子教案

    - **线程同步与通信**:在多线程环境下,Java提供了synchronized关键字、wait()、notify()等机制来保证线程安全和同步。 5. **网络应用协议** - **HTTP协议**:超文本传输协议,用于浏览器与Web服务器之间的通信...

    Java编程案例精解源代码

    6. **多线程**:Java提供了内置的多线程支持,可以通过实现Runnable接口或继承Thread类来创建和管理线程。 7. **Swing和JavaFX**:这两者是Java的图形用户界面(GUI)库,用于构建桌面应用程序。Swing是早期的选择...

    Java典型模块精解:电子相册

    在这个Java典型模块精解中,我们将深入探讨如何构建一个功能完备且用户体验良好的电子相册系统。 1. **Java基础**:电子相册的开发离不开Java语言的基础知识,包括类、对象、继承、多态等面向对象编程概念。Java的...

    java编程案例精解

    总之,“Java编程案例精解”涵盖了Java编程的诸多关键点,包括但不限于基本语法、异常处理、文件操作、集合框架、多线程、网络编程以及标准库的使用。通过实例学习,学习者不仅可以巩固理论知识,还能提升解决实际...

    Java编程案例精解

    在《Java编程案例精解》的光盘资料中,可能包含了大量的实战项目,比如简单的命令行应用、图形用户界面(GUI)程序、网络编程案例、多线程编程、数据库连接(JDBC)操作以及I/O流的使用。这些案例能帮助读者在实践中...

    Java编程案例精解素材.rar

    本资源“Java编程案例精解素材.rar”包含了一系列实用的Java编程示例,旨在帮助学习者深入理解和掌握Java的核心概念与技术。 1. **使用邮件客户端工具**:JavaMail API是Java中用于处理电子邮件的库,它允许开发者...

    Java知识体系精解

    Java多线程编程允许多个线程同时执行,提供了实现多线程的方法,包括继承Thread类和实现Runnable接口。wait和sleep方法都可以使线程挂起,但wait是Object类的方法,必须在同步方法或同步块中调用。notify()和...

    java网络精解源码

    这份"java网络精解源码"提供了深入理解和实践这些概念的机会。以下将详细阐述Java网络编程的一些关键知识点: 1. **Java网络API**: Java提供了一系列的API用于网络编程,包括Socket、ServerSocket、DatagramSocket...

    Java 集成开发实例精解

    8. **并发编程**:Java的并发库提供了线程、锁、同步工具,理解并发原理,掌握线程池的使用,可以编写出高效且安全的多线程程序。 9. **单元测试与持续集成**:JUnit和Mockito等工具用于编写单元测试,保证代码质量...

    Java网络编程精解之ServerSocket用法详解

    总结,Java的ServerSocket类是构建服务器端网络应用程序的基础,通过合理的构造方法和参数设置,可以实现对特定端口的监听,并使用多线程或线程池高效处理多个客户端的连接请求。了解和熟练掌握ServerSocket的使用,...

    Java 集成开发实例精解源码

    《Java集成开发实例精解源码》是一本深入探讨Java集成开发实践的资源集合,它包含了一系列实际项目中的源代码示例,旨在帮助开发者更好地理解和掌握Java在实际工程中的应用。通过对这些源码的分析和学习,开发者可以...

    Java 集成开发实例精解 Sour code

    《Java集成开发实例精解Source Code》是一本深入探讨Java集成开发实践的书籍,通过详细的源代码解析,帮助读者理解并掌握Java编程的核心技术和实际应用。在这个压缩包中,包含的文件名“java348990434”可能是书中...

    Java 集成开发实例精解 Sour code1

    5. **多线程编程**:Java提供了丰富的线程API,包括Thread类、Runnable接口以及并发包下的工具类,如ExecutorService、Semaphore等,用于实现高效的并发处理。 6. **网络编程**:如Socket通信,HTTP客户端/服务器端...

    javaSwing图形开发精解磁盘代码

    同时,Swing应用也可以利用SwingWorker来实现后台任务,避免阻塞UI线程,提升用户体验。 《Java Swing图形界面开发与案例详解》一书的源代码,无疑是一个宝贵的资源,它包含了各种Swing组件、布局管理和事件处理的...

    JAVA认证真题60道SCJP考试真题精解

    9. **多线程**:Java提供了Thread类和Runnable接口来实现多线程,理解并发概念、线程同步(synchronized关键字、wait/notify机制)和线程池。 10. **枚举与注解**:枚举是Java中的一种数据类型,用于定义有限的值集...

    java网络编程(非阻塞与阻塞编程)

    在深入探讨Java网络编程中的非阻塞与阻塞编程之前,我们先来了解这两个概念的基本含义。阻塞编程,通常指的是在程序执行过程中,当某一部分代码遇到I/O操作时,如读写文件或网络通信,整个程序会暂停运行,等待I/O...

    软件设计师考试-7.软件设计师考试试题分类精解(2018版).zip

    6. **编程语言与框架**:虽然没有特定指定某种语言,但通常软件设计师需要掌握至少一种或多种编程语言,如Java、C++或Python,以及相应的开发框架,如Spring、Django等。 7. **软件质量保证与测试**:理解测试的...

Global site tag (gtag.js) - Google Analytics