zoukankan      html  css  js  c++  java
  • Java中的线程

    目录

    线程

    一个线程的生命周期

    线程的创建与启动

    线程的终止

    线程的同步机制

    线程的通知机制


    线程

    一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

    这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

    多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

    一个线程的生命周期

    线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

    下图显示了一个线程完整的生命周期。

    • 新建状态:

      使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

    • 就绪状态:

      当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

    • 运行状态:

      如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

    • 阻塞状态:

      如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

      • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

      • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

      • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

    • 死亡状态:

      一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

    线程的创建与启动

    Java 提供了三种创建线程的方法:

    • 通过实现 Runnable 接口;
    • 通过继承 Thread 类本身;
    • 通过 Callable 和 Future 创建线程。

    方法一:实现Runnable接口

    这是因为 java 不允许多重继承,如果你的类已经继承于别的类,又要作为线程来运行,则可以使用这种方式

    public class Hello implements  Runnable{
           public void run(){
                   //填写要干的事
       }
    }
    Hello obj=new Hello();
    Thread t=new Thread(obj);
    t.start();                             //线程的启动

    方法二: 继承 Thread 类

    public class Hello  extends Thread{
            public void  run(){
                     //填写要干的事
         }
    }
    new Hello().start;               //线程的启动

    方法三:通过 Callable 和 Future 创建线程

    • 1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

    • 2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

    • 3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

    • 4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

    创建线程的三种方式的对比:

    • 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

    • 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

    线程的终止

    当一个线程的start()调用后,线程为Alive状态。当一个线程的主函数run()退出后,线程死亡(Dead);想让一个线程终止,就是要想办法让它从run()中退出来。当一个线程在sleep的时候,必须使用t.interrupt()来中断目标线程才可以使线程退出。等待线程的退出t.join();

    //线程的终止
    public class Test1 extends Thread{
      public boolean quitflag=false;
      public void run()
      {
        for(int i=0;i<10;i++){
           if(quitflag){         
             break;
           }
           System.out.println("---------");
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
         }
      }
    }
     
    public class Hello {
      public static void main(String[] args) {
        // TODO 自动生成的方法存根
        Test1 t1=new Test1();
        t1.start();
        //接受用户输入 输入后终止线程
        InputStreamReader m=new InputStreamReader(System.in);
        BufferedReader read=new BufferedReader(m);
        try {
           read.readLine();
           read.close();
           t1.quitflag=true;       //线程终止
           t1.interrupt();         //使正在sleep的线程终止
           t1.join();             //等待线程退出
        } catch (Exception e) {
           // TODO 自动生成的 catch 块
           e.printStackTrace();
        }
        System.out.println("结束了");
      }
    }
    

    线程的同步机制

    当两个或多个线程同时访问一个对象时,可能发生数据不同步的现象。为了实现多线程对同一对象的同步访问,引入互斥锁的概念。

    synchronized(lock)               //申请锁
    {         
                //关键代码
    }
    • 若别的线程正持有该锁,则本线程阻塞等待
    • 若此锁空闲,则本线程持有锁,进入大括号执行,完毕后释放锁

    线程的通知机制

    wait/notify 通知机制 

    1. 调用 wait(),notify(),notifyAll()时需要先对调用对象加锁。
    2. 调用wait()方法后,线程由RUNNING变为WAITING,并将当前线程放置于对象等待队列,释放锁
    3. notify(),notifyAll()方法被调用后,等待线程依然不会从wait()方法返回,而是等调用notify(),notifyAll()的线程释放该锁之后,等待线程才有机会从wait()返回。
    4. notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而 notifyAll()方法则是把所有等待线程从等待队列中移到同步队列中,被移动的线程的状态由WAITING变成BLOCKED
    5. 从wait()方法返回的前提是获得了调用对象的锁。
      public class Test1 {
        static boolean flag=true;
        static Object lock=new Object();
       
        public static void main(String[] args) {
          One a=new One();
          a.start();
          Two b=new Two();
          b.start();
        }
        
        static class One extends Thread{
          public void run(){
             synchronized(lock){
               while(flag){
                 try {
                   System.out.println("flag is true");
                   lock.wait();       //线程等待,释放锁
                 } catch (InterruptedException e) {
                   e.printStackTrace();
                 }
               }
               System.out.println("结束");
             }
           }
          }
        static class Two extends Thread{
          public void run(){
             synchronized (lock) {
               System.out.println("flag is false");
               flag=false;
               lock.notify();
               System.out.println("notify之后,不会立即退出!");
             }
          }
        }
      }
      
  • 相关阅读:
    转-- js(jQuery)获取时间的方法及常用时间类
    jquery 选择器(name,属性,元素)大全
    CSS中LI圆点样式li {list-style-type:符号名称}
    [华为oj]称砝码
    c++中的字符串与数字相互转换
    [华为]DP合唱队形
    [hihoCoder#1032]最长回文子串
    [STL系列]仿函数
    [字符串]寻找一个字符串中最大的公共子串
    [STL系列]STL容器性能比较列表
  • 原文地址:https://www.cnblogs.com/csnd/p/11807605.html
Copyright © 2011-2022 走看看