`

Java 多线程(一)

    博客分类:
  • JAVA
阅读更多
1、向服务器上程序都是采用多线程做的,为什么采用多线程呢,就是提高程序运行速度,就说访问数据库吧,服务器可以给每个连接数据库的客户端创建一个线程,然后这个线程就负责这个客户端数据的插入,查询,等操作,每一个客户端访问数据库都会创建一个线程,cpu采用分时操作,所以看起来好象同时多个客户端同时操作数据库一样,提高了访问速度。





2. Java中的线程由三部分组成
        1.   虚拟的CPU,封装在java.lang.Thread类中。
  2.   CPU所执行的代码,传递给Thread类。
  3.   CPU所处理的数据,传递给Thread类。
Java的线程是通过java.lang.Thread类来实现的。当我们生成一个Thread类的对象之后,一个新的线程就产生了。
它的构造方法:
public   Thread   (ThreadGroup   group,Runnable   target,String   name);
其中,group   指明该线程所属的线程组;target实际执行线程体的目标对象,它必须实现接口Runnable;   name为线程名。如果name为null时,则Java自动提供唯一的名称。

一个类声明实现Runnable接口就可以充当线程体,在接口Runnable中只定义了一个方法   run():  
  public   void   run();
每个线程都是通过某个特定Thread对象的方法run(   )来完成其操作的,方法run(   )称为线程体
1.   创建状态(new   Thread)
  执行下列语句时,线程就处于创建状态:
  Thread   myThread   =   new   MyThreadClass(   );
  当一个线程处于创建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。

  2.   可运行状态(   Runnable   )
  Thread   myThread   =   new   MyThreadClass(   );
  myThread.start(   );
  当一个线程处于可运行状态时,系统为这个线程分配了它需的系统资源,安排其运行并调用线程运行方法,这样就使得该线程处于可运行(   Runnable   )状态。需要注意的是这一状态并不是运行中状态(Running   ),因为线程也许实际上并未真正运行。由于很多计算机都是单处理器的,所以要在同一时刻运行所有的处于可运行状态的线程是不可能的,Java的运行系统必须实现调度来保证这些线程共享处理器。
  
  3.   不可运行状态(Not   Runnable)
  进入不可运行状态的原因有如下几条:
  1)   调用了sleep()方法;
  2)   调用了suspend()方法;
  3)   为等候一个条件变量,线程调用wait()方法;
  4)   输入输出流中发生线程阻塞;
  不可运行状态也称为阻塞状态(Blocked(输入/)。因为某种原因输出、等待消息或其它阻塞情况),系统不能执行线程的状态。这时即使处理器空闲,也不能执行该线程。

  4.   死亡状态(Dead)
  线程的终止一般可通过两种方法实现:自然撤消(线程执行完)或是被停止(调用stop()方法)。目前不推荐通过调用stop()来终止线程的执行,而是让线程执行完。
class   SimpleThread   extends   Thread   {
        public   SimpleThread(String   str)   {
                super(str);
        }
        public   void   run()   {
                for(int   i   =   0;   i   <   10;   i++)   {
                System.out.println(i   +   "   "   +   getName());
                try   {
                        sleep((int)(Math.random()   *   1000));
                }   catch   (InterruptedException   e)   {}
                }
               
                System.out.println( "DONE!   "   +   getName());
        }
}
public   class   TwoThreadsTest   {
        public   static   void   main   (String[]   args)   {
      new   SimpleThread( "First ").start();
      new   SimpleThread( "Second ").start();
        }
       
}  
两个线程是交错运行的,感觉就象是两个线程在同时运行。但是实际上一台计算机通常就只有一个CPU,在某个时刻只能是只有一个线程在运行。
在编程时要注意给每个线程执行的时间和机会,主要是通过让线程睡眠的办法(调用sleep()方法)来让当前线程暂停执行,然后由其它线程来争夺执行的机会。如果上面的程序中没有用到sleep()方法,则就是第一个线程先执行完毕,然后第二个线程再执行完毕。
两种方法实现线程体
1. 定义一个线程类,它继承线程类Thread并重写其中的方法   run()
2. 提供一个实现接口Runnable的类作为一个线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体   run()。
public   class   Clock   extends   java.applet.Applet   implements   Runnable   {
Thread   clockThread;
clockThread   =   new   Thread(this,   "Clock ");
/*线程体是Clock对象本身,线程名字为 "Clock "*/
clockThread.start();   //启动线程

3. 构造线程体的两种方法的比较:

  1.   使用Runnable接口
   1)   可以将CPU,代码和数据分开,形成清晰的模型;
   2)   还可以从其他类继承;
   3)   保持程序风格的一致性。

  2.   直接继承Thread类
   1)   不能再从其他类继承;
   2)   编写简单,可以直接操纵线程,无需使用Thread.currentThread()。
4. 线程的调度
        线程调度器按线程的优先级高低选择高优先级线程(进入运行中状态)执行,同时线程调度是抢先式调度,即如果在当前线程执行过程中,一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行。
抢先式调度又分为:时间片方式和独占方式。在时间片方式下,当前活动线程执行完当前时间片后,如果有其他处于就绪状态的相同优先级的线程,系统会将执行权交给其他就绪态的同优先级线程;当前活动线程转入等待执行队列,等待下一个时间片的调度。
在独占方式下,当前活动线程一旦获得执行权,将一直执行下去,直到执行完毕或由于某种原因主动放弃CPU,或者是有一高优先级的线程处于就绪状态。
下面几种情况下,当前线程会放弃CPU:

  1.   线程调用了yield()   或sleep()   方法主动放弃;  

  2.   由于当前线程进行I/O   访问,外存读写,等待用户输入等操作,导致线程阻塞;或者是为等候一个条件变量,以及线程调用wait()方法;  

  3.   抢先式系统下,由高优先级的线程参与调度;时间片方式下,当前时间片用完,由同优先级的线程参与调度
线程的优先级
线程的优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。一个线程的缺省优先级是5,即Thread.NORM_PRIORITY
int   getPriority();   //得到线程的优先级
  void   setPriority(int   newPriority);  
  //当线程被创建后,可通过此方法改变线程的优先级
一个线程在空闲时应该主动放弃CPU,以使其他同优先级和低优先级的线程得到执行
public   class   ThreadTest{
public   static   void   main   (String[]   args)   {
Thread   t1   =   new   MyThread( "T1 ");
t1.setPriority(   Thread.MIN_PRIORITY   );
t1.start(   );
Thread   t2   =   new   MyThread( "T2 ");
t2.setPriority(   Thread.MAX_PRIORITY   );
t2.start(   );
Thread   t3   =   new   MyThread( "T3 ");
t3.setPriority(   Thread.MAX_PRIORITY   );
t3.start(   );
}
}
class   MyThread   extends   Thread   {
String   message;
MyThread   (String   message)   {
this.message   =   message;
}
public   void   run()   {
for   (   int   i=0;   i <3;   i++   )
System.out.println(   message+ "   "+getPriority()   );
}
}

1.终止线程

  线程终止后,其生命周期结束了,即进入死亡态,终止后的线程不能再被调度执行,以下几种情况,线程进入终止状态:
  1)   线程执行完其run()方法后,会自然终止。
  2)   通过调用线程的实例方法stop()来终止线程。
2.   测试线程状态  

  可以通过Thread   中的isAlive()   方法来获取线程是否处于活动状态;线程由start()   方法启动后,直到其被终止之间的任何时刻,都处于 'Alive '状态。  

 3.   线程的暂停和恢复

  有几种方法可以暂停一个线程的执行,在适当的时候再恢复其执行。
  1)   sleep()   方法
  当前线程睡眠(停止执行)若干毫秒,线程由运行中状态进入不可运行状态,停止执行时间到后线程进入可运行状态。

  2)   suspend()和resume()方法
  线程的暂停和恢复,通过调用线程的suspend()方法使线程暂时由可运行态切换到不可运行态,若此线程想再回到可运行态,必须由其他线程调用resume()方法来实现。
  注:从JDK1.2开始就不再使用suspend()和resume()。

  3)   join()
  当前线程等待调用该方法的线程结束后,   再恢复执行.
  TimerThread   tt=new   TimerThread(100);
  tt.start();
  …
  public   void   timeout(){
  tt.join();//   当前线程等待线程tt   执行完后再继续往下执行  
  …   }











临界资源问题

  前面所提到的线程都是独立的,而且异步执行,也就是说每个线程都包含了运行时所需要的数据或方法,而不需要外部的资源或方法,也不必关心其它线程的状态或行为。但是经常有一些同时运行的线程需要共享数据,此时就需考虑其他线程的状态和行为,否则就不能保证程序的运行结果的正确性

为解决操作的不完整性问题,在Java   语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为 "   互斥锁 "   的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。   关键字synchronized   来与对象的互斥锁联系。当某个对象用synchronized   修饰时,表明该对象在任一时刻只能由一个线程访问。  
互斥锁
class   stack{
    public   void   push(char   c){
    synchronized(this){   //this表示Stack的当前对象
      data[idx]=c;
      idx++;
             }
    }
    public   char   pop(){
      synchronized(this){   //this表示Stack的当前对象
      idx--;
      return   data[idx];
      }
    }
}
  synchronized   除了象上面讲的放在对象前面限制一段代码的执行外,还可以放在方法声明中,表示整个方法为同步方法。
  public   synchronized   void   push(char   c){
  …
    }

  如果synchronized用在类声明中,则表明该类中的所有方法都是synchronized的。
多线程的同步
         class   SyncStack{   //同步堆栈类
   private   int   index   =   0;   //堆栈指针初始值为0
   private   char   []buffer   =   new   char[6];   //堆栈有6个字符的空间

   public   synchronized   void   push(char   c){   //加上互斥锁
     while(index   =   =   buffer.length){   //堆栈已满,不能压栈
     try{
        this.wait();   //等待,直到有数据出栈
       }catch(InterruptedException   e){}
       }
   this.notify();   //通知其它线程把数据出栈
   buffer[index]   =   c;   //数据入栈
   index++;   //指针向上移动
   }
   public   synchronized   char   pop(){   //加上互斥锁
       while(index   ==0){   //堆栈无数据,不能出栈
        try{
           this.wait();   //等待其它线程把数据入栈
        }catch(InterruptedException   e){}
          }
       this.notify();   //通知其它线程入栈
       index-   -;   //指针向下移动
       return   buffer[index];   //数据出栈
    }
       }
    class   Producer   implements   Runnable{   //生产者类
       SyncStack   theStack;  
        //生产者类生成的字母都保存到同步堆栈中

       public   Producer(SyncStack   s){
          theStack   =   s;
       }
       public   void   run(){
          char   c;
          for(int   i=0;   i <20;   i++){
            c   =(char)(Math.random()*26+ 'A ');  
                          //随机产生20个字符
            theStack.push(c);   //把字符入栈
            System.out.println( "Produced:   "+c);   //打印字符
            try{
            Thread.sleep((int)(Math.random()*1000));
                     /*每产生一个字符线程就睡眠*/
            }catch(InterruptedException   e){}
          }
       }
     }
     class   Consumer   implements   Runnable{   //消费者类
         SyncStack   theStack;  
                  //消费者类获得的字符都来自同步堆栈

         public   Consumer(SyncStack   s){
             theStack   =   s;
         }
         public   void   run(){
             char   c;
             for(int   i=0;i <20;i++){
               c   =   theStack.pop();   //从堆栈中读取字符
             System.out.println( "Consumed:   "+c);  
                             //打印字符
             try{
             Thread.sleep((int)(Math.random()*1000));  
                    /*每读取一个字符线程就睡眠*/
             }catch(InterruptedException   e){}
         }
       }
     }
     public   class   SyncTest{
       public   static   void   main(String   args[]){
         SyncStack   stack   =   new   SyncStack();
   //下面的消费者类对象和生产者类对象所操作的是同一个同步堆栈对象
         Runnable   source=new   Producer(stack);
         Runnable   sink   =   new   Consumer(stack);
         Thread   t1   =   new   Thread(source);   //线程实例化
         Thread   t2   =   new   Thread(sink);   //线程实例化
         t1.start();   //线程启动
         t2.start();   //线程启动
       }
     }
通过运用wait()和notify()方法来实现线程的同步,在同步中还会用到notifyAll()方法
(1)   wait,nofity,notifyAll必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用的范围内,也就是出现在用synchronized修饰的方法或类中。

  (2)   wait的作用:释放已持有的锁,进入等待队列.
  
  (3)   notify的作用:唤醒wait队列中的第一个线程并把它移入锁申请队列.

  (4)   notifyAll的作用:唤醒wait队列中的所有的线程并把它们移入锁申请队列.

堵塞
(1) 调用sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。
(2)   用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回“可运行”状态。
(3)   用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成“可运行”(是的,这看起来同原因2非常相象,但有一个明显的区别是我们马上要揭示的)。
(4)   线程正在等候一些IO(输入输出)操作完成。
(5)   线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。
分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java 多线程操作数据库

    本文将基于一个具体的Java多线程操作数据库的应用程序,深入探讨其背后的原理、实现细节以及潜在的挑战。 #### 核心知识点: 1. **多线程基础**:多线程是Java编程中的一个重要概念,允许程序同时执行多个任务。在...

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    Java多线程知识点总结

    Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    java多线程经典案例

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,极大地提升了程序的效率和性能。在Java中,实现多线程有两种主要方式:通过实现Runnable接口或者继承Thread类。本案例将深入探讨Java多线程中的关键...

    java多线程分页查询

    通过对上述代码的分析可以看出,该方案充分利用了Java多线程技术的优势,通过预加载前一页的数据,大大提高了后续请求的响应速度。然而,在实际应用中还需要注意以下几点: - **线程池管理**:频繁地创建新线程可能...

    java多线程Demo

    Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...

    汪文君JAVA多线程编程实战(完整不加密)

    《汪文君JAVA多线程编程实战》是一本专注于Java多线程编程的实战教程,由知名讲师汪文君倾力打造。这本书旨在帮助Java开发者深入理解和熟练掌握多线程编程技术,提升软件开发的效率和质量。在Java平台中,多线程是...

    java多线程查询数据库

    综上所述,"java多线程查询数据库"是一个涉及多线程技术、线程池管理、并发控制、分页查询等多个方面的复杂问题。通过理解和掌握这些知识点,我们可以有效地提高数据库操作的效率和系统的响应速度。

    java多线程处理数据库数据

    在Java编程中,多线程处理是提升程序性能和效率的重要手段,特别是在处理大量数据库数据时。本主题将深入探讨如何使用Java的并发包(java.util.concurrent)来实现多线程对数据库数据的批量处理,包括增、删、改等...

    深入浅出 Java 多线程.pdf

    在本文中,我们将深入浅出Java多线程编程的世界,探索多线程编程的基本概念、多线程编程的优点、多线程编程的缺点、多线程编程的应用场景、多线程编程的实现方法等内容。 一、多线程编程的基本概念 多线程编程是指...

    java 多线程并发实例

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

    JAVA单线程多线程

    ### JAVA中的单线程与多线程概念解析 #### 单线程的理解 在Java编程环境中,单线程指的是程序执行过程中只有一个线程在运行。这意味着任何时刻只能执行一个任务,上一个任务完成后才会进行下一个任务。单线程模型...

    JAVAJAVA多线程教学演示系统论文

    《JAVA多线程教学演示系统》是一篇深入探讨JAVA多线程编程的论文,它针对教育领域中的教学需求,提供了一种生动、直观的演示方式,帮助学生更好地理解和掌握多线程技术。这篇论文的核心内容可能包括以下几个方面: ...

    JAVA多线程练习题答案。

    JAVA多线程练习题答案详解 在本文中,我们将对 JAVA 多线程练习题的答案进行详细的解释和分析。这些题目涵盖了 JAVA 多线程编程的基本概念和技术,包括线程的生命周期、线程同步、线程状态、线程优先级、线程安全等...

    JAVA多线程编程技术PDF

    这份“JAVA多线程编程技术PDF”是学习和掌握这一领域的经典资料,涵盖了多线程的全部知识点。 首先,多线程的核心概念包括线程的创建与启动。在Java中,可以通过实现Runnable接口或继承Thread类来创建线程。创建后...

    Java多线程实现异步调用实例

    总之,Java多线程和异步调用是构建高效、响应迅速的应用程序的关键技术。通过合理利用这些工具和机制,开发者可以编写出能够充分利用多核处理器优势的代码,从而提高软件性能。在实际应用中,理解并熟练掌握这些概念...

    java多线程实现大批量数据导入源码

    本项目以"java多线程实现大批量数据导入源码"为题,旨在通过多线程策略将大量数据切分,并进行并行处理,以提高数据处理速度。 首先,我们需要理解Java中的线程机制。Java通过`Thread`类来创建和管理线程。每个线程...

Global site tag (gtag.js) - Google Analytics