`

Java多线程学习笔记

阅读更多
一、线程类
  Java是通过Java.lang.Thread类来实现多线程的,第个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法:
    1、需要从Java.lang.Thread类继承一个新的线程类,重载它的run()方法;
    2、通过Runnalbe接口实现一个从非线程类继承来类的多线程,重载Runnalbe接口的run()方法。运行一个新的线程,只需要调用它的start()方法即可。如:
/**=====================================================================
* 文件:ThreadDemo_01.java
* 描述:产生一个新的线程
* ======================================================================
*/
class ThreadDemo extends Thread{
  Threads()
  {
  }
  
  Threads(String szName)
  {
    super(szName);
  }
  
  // 重载run函数
  public void run()
  {
     for (int count = 1,row = 1; row < 20; row++,count++)
     {
        for (int i = 0; i < count; i++)
        {
           System.out.print('*');
        }
        System.out.println();
     }
  }
}

class ThreadMain{
  public static void main(String argv[]){
    ThreadDemo th = new ThreadDemo();
    // 调用start()方法执行一个新的线程
    th.start();
  }
}
  线程类的一些常用方法:

  sleep(): 强迫一个线程睡眠N毫秒。
  isAlive(): 判断一个线程是否存活。
  join(): 等待线程终止。
  activeCount(): 程序中活跃的线程数。
  enumerate(): 枚举程序中的线程。
    currentThread(): 得到当前线程。
  isDaemon(): 一个线程是否为守护线程。
  setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
  setName(): 为线程设置一个名称。
  wait(): 强迫一个线程等待。
  notify(): 通知一个线程继续运行。
  setPriority(): 设置一个线程的优先级。

二、等待一个线程的结束
  有些时候我们需要等待一个线程终止后再运行我们的另一个线程,这时我们应该怎么办呢?请看下面的例子:
/**=====================================================================
* 文件:ThreadDemo_02.java
* 描述:等待一个线程的结束
* ======================================================================
*/
class ThreadDemo extends Thread{
  Threads()
  {
  }
  
  Threads(String szName)
  {
    super(szName);
  }
  
  // 重载run函数
  public void run()
  {
     for (int count = 1,row = 1; row < 20; row++,count++)
     {
        for (int i = 0; i < count; i++)
        {
           System.out.print('*');
        }
        System.out.println();
     }
  }
}

class ThreadMain{
  public static void main(String argv[]){
    //产生两个同样的线程
    ThreadDemo th1 = new ThreadDemo();
    ThreadDemo th2 = new ThreadDemo();

   // 我们的目的是先运行第一个线程,再运行第二个线程
   th1.start();
   th2.start();
  }
}

这里我们的目标是要先运行第一个线程,等第一个线程终止后再运行第二个线程,而实际运行的结果是如何的呢?实际上我们运行的结果并不是两个我们想要的直角三角形,而是一些乱七八糟的*号行,有的长,有的短。为什么会这样呢?因为线程并没有按照我们的调用顺序来执行,而是产生了线程赛跑现象。实际上Java并不能按我们的调用顺序来执行线程,这也说明了线程是并行执行的单独代码。如果要想得到我们预期的结果,这里我们就需要判断第一个线程是否已经终止,如果已经终止,再来调用第二个线程。代码如下:
/**=====================================================================
* 文件:ThreadDemo_03.java
* 描述:等待一个线程的结束的两种方法
* ======================================================================
*/
class ThreadDemo extends Thread{
  Threads()
  {
  }
  
  Threads(String szName)
  {
    super(szName);
  }
  
  // 重载run函数
  public void run()
  {
     for (int count = 1,row = 1; row < 20; row++,count++)
     {
        for (int i = 0; i < count; i++)
        {
           System.out.print('*');
        }
        System.out.println();
     }
  }
}

class ThreadMain{
  public static void main(String argv[]){
    ThreadMain test = new ThreadMain();
    test.Method1();
    // test.Method2();
  }
  // 第一种方法:不断查询第一个线程是否已经终止,如果没有,则让主线程睡眠一直到它终止为止
 // 即:while/isAlive/sleep
 
public void Method1(){
    ThreadDemo th1 = new ThreadDemo();
    ThreadDemo th2 = new ThreadDemo();
    // 执行第一个线程
    th1.start();
    // 不断查询第一个线程的状态
    while(th1.isAlive()){
      try{
         Thread.sleep(100);
      }catch(InterruptedException e){
      }
    }
    //第一个线程终止,运行第二个线程
    th2.start();
  }
  
  // 第二种方法:join()
  public void Method2(){
    ThreadDemo th1 = new ThreadDemo();
    ThreadDemo th2 = new ThreadDemo();
    // 执行第一个线程
    th1.start();
    try{
      th1.join();
    }catch(InterruptedException e){
    }
    // 执行第二个线程
  th2.start();
}

三、线程的同步问题

   有些时候,我们需要很多个线程共享一段代码,比如一个私有成员或一个类中的静态成员,但是由于线程赛跑的问题,所以我们得到的常常不是正确的输出结果,而相反常常是张冠李戴,与我们预期的结果大不一样。看下面的例子:
/**=============================================================================
* 文件:ThreadDemo_04.java
* 描述:多线程不同步的原因
* =============================================================================
*/
// 共享一个静态数据对象
class ShareData{
 public static String szData = "";
}

class ThreadDemo extends Thread{
  
  private ShareData oShare;

  ThreadDemo(){
  }

  ThreadDemo(String szName,ShareData oShare){
    super(szName);
    this.oShare = oShare;
  }

  public void run(){
    // 为了更清楚地看到不正确的结果,这里放一个大的循环
  for (int i = 0; i < 50; i++){
       if (this.getName().equals("Thread1")){
         oShare.szData = "这是第 1 个线程";
         // 为了演示产生的问题,这里设置一次睡眠
     try{
           Thread.sleep((int)Math.random() * 100);
         catch(InterruptedException e){
         }
         // 输出结果
         System.out.println(this.getName() + ":" + oShare.szData);
       }else if (this.getName().equals("Thread2")){
         oShare.szData = "这是第 1 个线程";
         // 为了演示产生的问题,这里设置一次睡眠
     try{
           Thread.sleep((int)Math.random() * 100);
         catch(InterruptedException e){
         }
         // 输出结果
         System.out.println(this.getName() + ":" + oShare.szData);
       }
   }
}

class ThreadMain{
  public static void main(String argv[]){
    ShareData oShare = new ShareData();
    ThreadDemo th1 = new ThreadDemo("Thread1",oShare);
    ThreadDemo th2 = new ThreadDemo("Thread2",oShare);

    th1.start();
    th2.start();
  }
}

  由于线程的赛跑问题,所以输出的结果往往是Thread1对应“这是第 2 个线程”,这样与我们要输出的结果是不同的。为了解决这种问题(错误),Java为我们提供了“锁”的机制来实现线程的同步。锁的机制要求每个线程在进入共享代码之前都要取得锁,否则不能进入,而退出共享代码之前则释放该锁,这样就防止了几个或多个线程竞争共享代码的情况,从而解决了线程的不同步的问题。可以这样说,在运行共享代码时则是最多只有一个线程进入,也就是和我们说的垄断。锁机制的实现方法,则是在共享代码之前加入synchronized段,把共享代码包含在synchronized段中。上述问题的解决方法为:

/**=============================================================================
* 文件:ThreadDemo_05.java
* 描述:多线程不同步的解决方法--锁
* =============================================================================
*/
// 共享一个静态数据对象
class ShareData{
 public static String szData = "";
}

class ThreadDemo extends Thread{
  
  private ShareData oShare;

  ThreadDemo(){
  }

  ThreadDemo(String szName,ShareData oShare){
    super(szName);
    this.oShare = oShare;
  }

  public void run(){
    // 为了更清楚地看到不正确的结果,这里放一个大的循环
  for (int i = 0; i < 50; i++){
       if (this.getName().equals("Thread1")){
         // 锁定oShare共享对象
         synchronized (oShare){
           oShare.szData = "这是第 1 个线程";
           // 为了演示产生的问题,这里设置一次睡眠
       try{
             Thread.sleep((int)Math.random() * 100);
           catch(InterruptedException e){
           }
           // 输出结果
           System.out.println(this.getName() + ":" + oShare.szData);
         }
       }else if (this.getName().equals("Thread2")){
         // 锁定共享对象
         synchronized (oShare){
           oShare.szData = "这是第 1 个线程";
           // 为了演示产生的问题,这里设置一次睡眠
       try{
             Thread.sleep((int)Math.random() * 100);
           catch(InterruptedException e){
           }
           // 输出结果
           System.out.println(this.getName() + ":" + oShare.szData);
         }
       }
   }
}

class ThreadMain{
  public static void main(String argv[]){
    ShareData oShare = new ShareData();
    ThreadDemo th1 = new ThreadDemo("Thread1",oShare);
    ThreadDemo th2 = new ThreadDemo("Thread2",oShare);

    th1.start();
    th2.start();
  }
}

  由于过多的synchronized段将会影响程序的运行效率,因此引入了同步方法,同步方法的实现则是将共享代码单独写在一个方法里,在方法前加上synchronized关键字即可。

  在线程同步时的两个需要注意的问题:
  1、无同步问题:即由于两个或多个线程在进入共享代码前,得到了不同的锁而都进入共享代码而造成。
  2、死锁问题:即由于两个或多个线程都无法得到相应的锁而造成的两个线程都等待的现象。这种现象主要是因为相互嵌套的synchronized代码段而造成,因此,在程序中尽可能少用嵌套的synchronized代码段是防止线程死锁的好方法。

  在写上面的代码遇到的一个没能解决的问题,在这里拿出来,希望大家讨论是什么原因。

/**=============================================================================
* 文件:ThreadDemo_06.java
* 描述:为什么造成线程的不同步。
* =============================================================================
*/
class ThreadDemo extends Thread{
  //共享一个静态数据成员
  private static String  szShareData = "";

  ThreadDemo(){
  }

  ThreadDemo(String szName){
    super(szName);
  }

  public void run(){
    // 为了更清楚地看到不正确的结果,这里放一个大的循环
  for (int i = 0; i < 50; i++){
       if (this.getName().equals("Thread1")){
     synchronized(szShareData){
           szShareData = "这是第 1 个线程";
           // 为了演示产生的问题,这里设置一次睡眠
       try{
             Thread.sleep((int)Math.random() * 100);
           catch(InterruptedException e){
           }
           // 输出结果
           System.out.println(this.getName() + ":" + szShareData);
        }
       }else if (this.getName().equals("Thread2")){
         synchronized(szShareData){
           szShareData = "这是第 1 个线程";
           // 为了演示产生的问题,这里设置一次睡眠
       try{
             Thread.sleep((int)Math.random() * 100);
           catch(InterruptedException e){
           }
           // 输出结果
           System.out.println(this.getName() + ":" + szShareData);
         }
       }
   }
}

class ThreadMain{
  public static void main(String argv[]){
    ThreadDemo th1 = new ThreadDemo("Thread1");
    ThreadDemo th2 = new ThreadDemo("Thread2");

    th1.start();
    th2.start();
  }
}
  这段代码的共享成员是一个类中的静态成员,按理说,这里进入共享代码时得到的锁应该是同样的锁,而实际上以上程序的输入却是不同步的,为什么呢??
分享到:
评论

相关推荐

    JAVA 多线程学习笔记

    这篇学习笔记将深入探讨Java多线程的核心概念、实现方式以及相关工具的使用。 一、多线程基础 1. 线程与进程:在操作系统中,进程是资源分配的基本单位,而线程是程序执行的基本单位。每个进程至少有一个主线程,...

    java多线程学习笔记

    这篇文档和对应的源代码 博文链接:https://interper56-sohu-com.iteye.com/blog/172303

    多线程学习笔记

    多线程学习笔记 iOS开发中,多线程是一种常见的技术手段,用于优化应用程序的性能,提升用户体验。多线程的核心是让程序能够并发地执行多个任务,合理地利用设备的计算能力,尤其是在拥有多个核心的处理器上。 ...

    java学习笔记2(多线程)

    java学习笔记2(多线程)java学习笔记2(多线程)

    java多线程学习笔记02(csdn)————程序.pdf

    Java多线程编程是开发高并发应用的关键技术之一。在这个学习笔记中,主要讨论了Java中的线程同步机制,包括volatile关键字、synchronized以及Lock接口,特别是ReentrantLock的使用。 首先,对于线程1和线程2的疑惑...

    JAVA多线程学习笔记整理(csdn)————程序.pdf

    Java多线程是Java编程中的核心概念,它允许并发执行多个任务,提高程序的执行效率。以下是关于Java多线程的详细知识点: 1. **创建线程** - **继承Thread类**:创建一个新的类,该类继承自Thread类,并重写run()...

    java多线程学习笔记之自定义线程池

    Java多线程学习笔记之自定义线程池 本篇文章主要介绍了Java多线程学习笔记之自定义线程池,通过深入了解ThreadPoolExecutor这个核心类,我们可以自定义线程池,满足不同的线程池需求。 Java多线程学习笔记之自定义...

    Java并发编程学习笔记 pdf 多线程编程

    Java并发编程学习笔记,研究JAVA并发多线程编程的一本教程,使用并发技术可以开发出并行算法,充分利用多处理器的计算能力,避免硬件资源浪费。目前,在JAVA并发编程方面的论述系统且内容详实的技术资料不太多,Java...

    java 多线程的基础知识

    java 多线程学习笔记

    java多线程笔记

    线程同步是为了避免多线程环境下的数据竞争问题,Java提供了多种同步机制。同步方法通过`synchronized`关键字修饰,确保同一时间只有一个线程能访问该方法。同步块(Synchronized Block)更灵活,可以指定同步的代码...

    基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码.zip

    基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码.zip 基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码.zip 基于java的开发源码-java多线程反射泛型及正则表达式学习笔记和源码....

    java多线程学习.txt

    java多线程学习笔记,主要记录多线程的一些基础概念,多线程简单的使用方式

    java基础:多线程学习笔记

    java基础:多线程学习笔记

    java线程学习笔记

    Java 线程学习笔记 Java 线程创建有两种方法: 1. 继承 Thread 类,重写 run 方法:通过继承 Thread 类并重写 run 方法来创建线程,这种方法可以使线程具有自己的执行逻辑。 2. 实现 Runnable 接口:通过实现 ...

    Java多线程文章系列.pdf

    #### 五、Java多线程学习笔记 ##### 1. 线程类 - **Thread类**: - 提供了创建和控制线程的方法。 - 可以通过继承`Thread`类或实现`Runnable`接口来创建线程。 ##### 2. 等待一个线程的结束 - **join()方法**: -...

    java学习笔记5(java多线程)

    java学习笔记5(java多线程)java学习笔记5(java多线程)

    learn-thread:Java多线程学习笔记

    学习线程介绍Java多线程学习PDF格式Java并发编程的艺术.pdf JAVA并发编程实践.pdf图解Java多线程设计模式-第2版.pdf代码code1是《 Java并发编程的艺术》的源码ThreadDesignPatterns是《图解Java多线程设计模式》第1...

Global site tag (gtag.js) - Google Analytics