• 线程基础 第一篇:线程的定义、状态、属性、简单实现线程


       本文主要讲了java中线程以及多线程的使用方法、线程同步、线程状态及线程间相互通信等。首先让我们来了解下在操作系统中的进程和线程:

    一、线程与进程的定义

      (1)进程:(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

      总结来说:

      进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)

       (2)1.线程:有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

               2.多线程:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程

        总结来说:

      线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

        多进程是指操作系统能同时运行多个任务(程序)。

        多线程是指在同一程序中有多个顺序流在执行。

    二、线程与进程的共同点和区别

      (1)共同点:

          线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

      (2)区别:

          线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程的运行中需要使用计算机的内存资源和CPU。

        线程与进程的区别可以归纳为以下几点:

          1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

          2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

          3)调度和切换:线程上下文切换比进程上下文切换要快得多。

          4)在多线程OS中,进程不是一个可执行的实体。

      (3)线程的状态:

          就绪:线程分配了CPU以外的全部资源,等待获得CPU调度

          执行:线程获得CPU,正在执行

            阻塞:线程由于发生I/O或者其他的操作导致无法继续执行,就放弃处理机,转入线程就绪

      (4)属性(特点)

          线程在多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。

        ①轻型实体

          线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源,比如,在每个线程中都应具有一个用于控制线程运行的线程控制块TCB,用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。

        独立调度和分派的基本单位。

          在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。

        可并发执行。

          在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行。

        共享进程资源。

          在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。

    三、简单的线程实现

      在java中要想实现线程,主要有两种手段,一种是继续Thread类,另外一种是实现Runable接口.好了,话不多说,下面我们开始演示demo:

        (一)演示背景:假设我们有一个卖票系统,共有50张票,现在只有一个窗口,那么写到代码中就是一个线程(窗口代表卖票的线程)

          1)继承Thread类的方法

    /**
     *@functon 卖票系统中的窗口 
     *@author 温煦(昵称:沉沦之巅)
     *@time 2017.12.1 
     */
    
    package TicketShop;
    
    //继承Thread类的线程
    public class SaleTickets extends Thread{
        
        //获得票的类
        public Tickets tic;
        
        //有参构造
        public SaleTickets(Tickets tic) {
            super();
            this.tic = tic;
        }
    
        //继承Thread中的run方法,进行重写
        @Override
        public  void run() {
            //while判断当票数为零时停止销售
            while(tic.getCount()>=0){
                sale();
            }
        }
    
        //卖票的方法
        public  void sale() {
            //判断当票数大于0时卖票
            if(tic.getCount()>0){
                //获取当前线程的名字,直观的看出是哪个线程,当然只有一个线程时可以不用
                String threadname = Thread.currentThread().getName();
                //打印输出卖的是第几张票
                System.out.println(threadname+":第"+tic.getCount()+"张票已售出!");
                //卖完之后要让票的总数减1
                tic.setCount(tic.getCount()-1);
                try {
                    //线程沉睡0.2秒,只是方便看演示效果
                    Thread.currentThread().sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{//当票数为0是输出“票已售空"
                //因为while还在循环,所以要减一,否则会进入“票已售空”无限死循环
                tic.setCount(tic.getCount()-1);
                System.out.println("票已售空!");
            }
        }
    
        //票类的set和get方法
        public Tickets getTic() {
            return tic;
        }
    
        public void setTic(Tickets tic) {
            this.tic = tic;
        }
    }

          2)实现Runnable接口的方法

    /**
    *@functon 卖票系统中的窗口 
    *@author 温煦(昵称:沉沦之巅)
    *@time 2017.12.1 
    */
    package TicketShop;
    //此类为实现Runnable的接口的方法来演示线程,
    //与上面的继承的Thread里面的属性与方法完全一样,注释我就不一一写了哦(手动捂脸,好懒)
    public class SaleTicketsbyRannable implements Runnable{
    
        public Tickets tic;
    
        public SaleTicketsbyRannable(Tickets tic) {
            super();
            this.tic = tic;
        }
    
        @Override
        public void run() {
            while(tic.getCount()>=0){
                sale();
            }
        }
        
        public  void sale() {
            
            if(tic.getCount()>0){
                String threadname = Thread.currentThread().getName();
                System.out.println(threadname+":第"+tic.getCount()+"张票已售出!");
                tic.setCount(tic.getCount()-1);
                try {
                    Thread.currentThread().sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                tic.setCount(tic.getCount()-1);
                System.out.println("票已售空!");
            }
        }
    
        public Tickets getTic() {
            return tic;
        }
    
    
        public void setTic(Tickets tic) {
            this.tic = tic;
        }
    
    }

         

         3)票的类   

     

    /**
     *@functon 卖票系统中的总票数 
     *@author 温煦(昵称:沉沦之巅)
     *@time 2017.12.1 
     */
    
    package TicketShop;
    
    //此类为要卖的票,所有要卖的票都要从这里取
    public class Tickets {
        //因是一个demo,所以票类里没有那么多的属性值
        //票的总数
        private int count;
    
        //有参构造
        public Tickets(int count) {
            super();
            this.count = count;
        }
        
        //无参构造
        public Tickets() {
            super();
        }
        
        //set和get方法
        public int getCount() {
            return count;
        }
        public void setCount(int count) {
            this.count = count;
        }
    }

        4)分别演示卖票结果

          1.演示继承Thread类的方法

    /**
     *@functon 卖票系统演示(用继承Thread类的方法)
     *@author 温煦(昵称:沉沦之巅)
     *@time 2017.12.1 
     */
    
    package TicketShop;
    
    public class TestSale {
    
        public static void  main(String[] args) {
            
            //new一个票的实体类,并给它50张票
            Tickets tic = new Tickets(50);
            //创建  
            SaleTickets st = new SaleTickets(tic);
            //因此对象已经继承Thread类所以可以直接调用
            //执行
            st.start();
        }
    }

         

         2.演示实现Rannble接口的方法

    /**
     *@functon 卖票系统演示(用实现Rannble接口的方法)
     *@author 温煦(昵称:沉沦之巅)
     *@time 2017.12.1 
     */
    
    package TicketShop;
    
    public class TestSale {
    
        public static void  main(String[] args) {
            
            //new一个票的实体类,并给它50张票
            Tickets tic = new Tickets(50);
            //创建  
            SaleTicketsbyRannable str = new SaleTicketsbyRannable(tic);
            //因此对象没有继承Thread类所以不可以直接调用
            //需要new一个Thread
            Thread st = new Thread(str);
            //执行
            st.start();
        }
    }

        5)演示效果:两种方法执行线程的演示效果均一样(因票数太多,就没有全部贴出来,不要怪我哦,如果想要验证的话,你可以自己尝试一下)

      Thread-0:第50张票已售出!
      Thread-0:第49张票已售出!

    ...
    ... ...

    Thread-0:第11张票已售出! Thread-0:第10张票已售出! Thread-0:第9张票已售出! Thread-0:第8张票已售出! Thread-0:第7张票已售出! Thread-0:第6张票已售出! Thread-0:第5张票已售出! Thread-0:第4张票已售出! Thread-0:第3张票已售出! Thread-0:第2张票已售出! Thread-0:第1张票已售出! 票已售空!

         如上演示效果所示:单个线程会依次执行任务,直到票已售完为止,这就是本篇的运用两种方法执行线程的小demo。

         如这篇你已经理解,那么恭喜你已经踏上了线程的第一节台阶,嘻嘻(悄悄告诉你哦,第二篇是实现线程之间的同步,如果有兴趣就去看看吧!

        声明:此篇以及之后几篇都为本人在学习和复习阶段整理笔记所写,包括上网搜的知识,所以如有侵权,请告知!

  • 相关阅读:
    集合框架整理及之间的区别
    ArrayList和LinkedList
    GC(Garbage Collection)
    Java常用工具类
    Java异常处理
    JDK环境配置
    内部类总结
    Java字符串定义及常用方法
    Java面向对象总结
    Java数组定义及方法
  • 原文地址:https://www.cnblogs.com/Wenxu/p/7942757.html
Copyright © 2020-2023  润新知