• 多线程概述


    多线程

    线程概述

    程序、进程、线程

    1、程序:指令集 静态概念

    2、进程:操作系统 调度程序 动态概念

                        每个进程都是独立的,有3部分组成:cpu,data(数据),code(代码区)

                        缺点:内存浪费,cpu的负担

    3、线程:Thread,是进程中一个“单一的连续控制流程”

                        一个进程可拥有多个并行的线程

                        一个进程中线程共享相同内存单元/内存地址空间-->可以访问相同的变量和对象,

                        而且它们从同一堆中分配对象-->通信、数据交换、同步操作

                        由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这

                        就使得通信更简便而且信息传递的速度也更快。

    线程与进程区别:

             1、根本区别:进程作为资源分配的单位,线程作为调度和执行的单位

             2、开销:每个进程有独立代码和数据空间,进程间的切换开销大

                                 线程是轻量级的进程,同以内线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程切换开销小

             3、分配内存:系统运行时候会给每个进程分配不同内存区域

                                          线程使用的资源是它所属的进程的资源,线程组只能共享资源

             4、包含关系:进程包含线程

    线程的创建

    实现多线程(一):继承Thread

    package cn.pb.thread.create;

     

    public classRabbit extendsThread{

        @Override

        public void run() {

            //线程体

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

                System.out.println("兔子跑了"+i+"");

            }

        }

    }

     

    class Torroise extends Thread{

        @Override

        public void run() {

            //线程体

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

                System.out.println("乌龟跑了"+i+"");

            }

        }

    }

     

    public static void main(String[] args) {

            //创建子类对象

            Rabbit rabbit = newRabbit();

            Torroise torroise = newTorroise();

            //调用start方法

            rabbit.start(); //不要调用run方法

            torroise.start();

        }

    实现多线程(二):实现Runnable接口(推荐)

     

    public classRabbit2 implementsRunnable{

        @Override

        public void run() {

            //线程体

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

                System.out.println("兔子跑了"+i+"");

            }

        }

    }

    class Torroise2 implements Runnable{

        @Override

        public void run() {

            //线程体

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

                System.out.println("乌龟跑了"+i+"");

            }

        }

    }

     

    public static void main(String[] args) {

            //1):创建真实角色

            Rabbit2 r = newRabbit2();

            Torroise2 t = newTorroise2();

            //2):创建代理角色 + 正式角色引用

            Thread proxyR = newThread(r);

            Thread proxyT = newThread(t);

            //3):调用start() 启动线程

            proxyR.start();

            proxyT.start();

        }

     

    小结:创建多线程方法

    一、继承Threand + run()

    启动:创建之内对象+ 对象.start()

    二、实现Runnable + run()

    启动:使用静态代理

    1、  创建真实对象

    2、  创建代理角色Thread+引用

    3、  代理角色.start()

    推荐使用实现runnable接口

    1、  避免单继承局限性

    2、  便于共享资源

    3、  通用 可以多实现,不能多继承

    静态代理

    package cn.pb.thread.create;

     

     

    public classStaticProxy {

        public static void main(String[] args) {

            //创建真实角色

            You you = newYou();

            //创建代理角色 + 真实角色的引用

            WeddingCampany wc = newWeddingCampany(you);

            //执行任务

            wc.marry();

        }

    }

     

    //接口

    interface Marry{

        public abstract void marry();

    }

    //真实角色

    class You implementsMarry{

        @Override

        public void marry() {

            System.out.println("you and 嫦娥.");

        }

    }

    //代理角色

    class WeddingCampany implements Marry{

        private Marry you;

        public WeddingCampany() {

        }

        public WeddingCampany(Marry you){

            this.you = you;

        }

        private void before(){

            System.out.println("布置猪窝.");

        }

        private void after(){

            System.out.println("闹玉兔.");

        }

        @Override

        public void marry() {

            before();

            you.marry();

            after();

        }

    }

     

    状态

    一、线程状态

    多线程/单例模式/生产者消费者模式

    新生状态:

    new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

    就绪状态:

    处于就绪状态的线程已经具备了运行条件,但还没有分配到cpu,处于线程就绪队列,等待系统为其分配cpu。等待状态不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行的状态进入执行状态。线程挑选的动作称之为“cpu调度。一旦获得cpu。线程就进入运行状态兵自动调用自己的run方法。

    运行状态:

             在运行状态的线程执行自己的run方法中代码。直到调用其他方法而终止,或等待某资源而阻塞或完成任务而死亡,如果在给定的时间片内没有执行结束,就会被系统换下来回到等待执行状态。

    阻塞状态:

             处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法。或等待I/O设备资源,将让出cpu并暂时停止自己的运行,并进入阻塞状态。在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠事件已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。

    死亡状态:

             死亡状态是线程生命周期中的最后一个阶段,线程死亡的原因有两个。一个是正常运行的线程完成了它全部工作;另一个是线程被强制性的终止,如通过指向stopdestory方法来终止一个线程(不推荐使用这两个方法,前者会产生异常,后者是强制终止,不会释放锁)。

     

    二、停止线程

    1、  自然终止:线程体正常执行完毕

    2、  外部干涉:

     1)、线程类中定义线程体使用的标识

     2)、线程体使用该标识

     3)、提供对外的方法改变该标识

    package cn.pb.thread.start;

     

     

    public classDemo1 {

        public static void main(String[] args) {

            Study study = newStudy();

            new Thread(study).start();

            //外部干涉

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

                if(i==50){     //外部干涉

                    study.stop();

                }

                System.out.println("main..."+i);

            }

        }

    }

     

    class Study implementsRunnable{

        //1.线程类中定义线程体使用的标识

        private boolean flag = true;

        @Override

        public void run() {

            //2.线程体使用该标识

            while(flag){

                System.out.println("study threaf...");

            }

        }

        //3.对外提供方法改变标识

        public void stop(){

            this.flag=false;

        }

    }

    三、阻塞

    1join:合并线程

    2yield:暂停自己线程  static

    3sleep:休眠,不释放锁

             1)、与时间相关:倒计时

             2)、模拟网络延时

     

    public classSleepDemo01 {

        public static void main(String[] args) throws InterruptedException {

            int num=10;

            while(true){

                System.out.println(num--);

                Thread.sleep(1000);     //暂停

                if(num<=0){

                    break;

                }

            }

        }

    }

     

     

    public classSleepDemo02 {

        public static void main(String[] args) throws InterruptedException {

            Date endTime = newDate(System.currentTimeMillis()+10*1000);

            long end = endTime.getTime();

            while(true){

                //输出

               

                System.out.println(new SimpleDateFormat("mm:ss").format(endTime));

                //构建下一秒时间

                endTime = newDate(endTime.getTime()-1000);

                //等待1

                Thread.sleep(1000);

                //10秒内继续否则退出

                if(end-10000>endTime.getTime()){

                    break;

                }

            }

        }

    }

    同步

    同步:并发 多个线程访问一分资源 确保资源安全-à线程安全

    synchronized  à同步

    一、 同步块

    synchronized(引用类型(this)类.class){

    二、 同步方法

    synchronized(this){

    }

    三、死锁:过多的同步容易导致死锁

     

    单例模式

     

    class Jvm{

        //声明一个私有的静态变量

        private static Jvm instance = null;

       

        //构造器私有化,避免外部直接创建对象

        private Jvm(){

        }

        //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象

        public static Jvm getInstance(){

           if(null==instance){      //提高已经存在对象的访问效率

               synchronized(Jvm.class){

                  if(null==instance){  //安全

                      instance = new Jvm();

                  }

               }

           }

           return instance;

        }

    }

     

    class Jvm2{

        private static Jvm2 instance = new Jvm2();

        private Jvm2(){

          

        }

        public static Jvm2 getInstance(){

           return instance;

        }

    }

    生产者消费者模式

    也称为限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程-----即所谓的生产者消费者”------在实际运行是会发生的问题。生产者的主要作用是生成一定量的数据。该问题的关键就是要保证生产者不会在缓冲区时加入数据,消费者也不会在缓冲区中空时消耗数据。

     

    要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常常用的方法有信号灯法,管程法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都将会陷入休眠,等待对方唤醒自己。

     

    public class Movie {

        private String pic;

       

        private boolean flag = true;

     

       

        public synchronized void play(String pic) {

            if(!flag){              //生产者等待

                try {

                    this.wait();

                } catch(InterruptedException e) {

                    e.printStackTrace();

                }

            }

            try {

                //开始生产

                Thread.sleep(500);

            } catch(InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println("生产了:"+pic);

            //生产完毕

            this.pic = pic;

            //通知消费

            this.notify();

            //生产者停下

            this.flag=false;

        }

     

        public synchronized void watch() {

            if(flag){               //消费者等待

                try {

                    this.wait();

                } catch(InterruptedException e) {

                    e.printStackTrace();

                }

            }

            try {

                //开始消费

                Thread.sleep(200);

            } catch(InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println("消费了:"+pic);

            //消费完毕

            //通知生产

            this.notifyAll();

            //消费停止

            this.flag=true;

        }

    }

     

    总结:

    创建线程的两种方式

    线程的状态:

             新生—>startà就绪—>运行-à阻塞-à终止

    线程终止

    阻塞: sleep(线程方法) (与waitObject方法)区别)

  • 相关阅读:
    2017寒假作业二 汇总随笔
    2017寒假作业一
    UVA 1601 POJ 3523 The Morning after Halloween 【双向BFS】【A*】 (好题)
    UVA 10570 Meeting with Aliens 【枚举+结论题】
    UVA 1614 Hell on the Markets 【贪心+结论题】
    UVA 10603 Fill【BFS】
    Codevs 1288 埃及分数 【IDA*】
    UVA 11212 Editing a Book 【IDA*】
    UVA 11624 Fire! 【特殊BFS】
    UVA 1599 Ideal Path 【两次BFS+贪心】 (好题)
  • 原文地址:https://www.cnblogs.com/dooor/p/5252251.html
Copyright © 2020-2023  润新知