多线程概述

多线程

线程概述

程序、进程、线程

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方法)区别)

原文地址:https://www.cnblogs.com/dooor/p/5252251.html