zoukankan      html  css  js  c++  java
  • java基础24 线程、多线程及线程的生命周期(Thread)

    1.1、进程

        正在执行的程序称作为一个进程.进程负责了内存空间的划分

     疑问1:windows电脑称之为多任务的操作系统,那么Windows是同时运行多个应用程序呢?
        从宏观的角度:windows确实在同时运行多个程序.
        从微观的角度:cpu是做一个快速的切换执行的动作,速度太快,所以你感觉不到切换而已.

    1.2、线程

        线程在一个进程中负责了代码的执行,进程中的一个执行路径

    1.3、多线程

        在一个进程中有多个线程同时在执行不同的任务

    疑问2:线程负责了代码的执行,我们之前没有学过线程,为什么代码可以执行?
       任何一个java程序,在jvm运行时都会创建一个main线程执行main方法中的所有代码.

    1.4、多线程好处

        1.解决一个进程中能执行多个任务的问题.
        2.提高了资源的利用率

    1.5、多线程弊端

        1.增加了cpu的负担
        2.降低了一个进程中的线程执行的概率
        3.引发了线程安全问题
        4.出现死锁的现象

    1.6、创建多线程的方式

    方式一:
        1.自定义一个类继承Thread类
        2.重写Thread的类里面的run方法,自定义线程的代码写在run方法里面
        3.创建Thread的子类对象,并且调用start方法开启线程.

      注意:一个线程一旦开启,那么这个线程就会执行run方法中的代码,run方法千万不要直接调用,如果直接调用它就相当于一个普通方法,就不会开启新的线程。

    疑问3: 重写run方法的目的是什么?
       每个线程都有自己的任务代码,jvm创建主线程的任务代码就是main方法中的所有代码,自定义线程的任务代码,需要写在run方法里面,自定线程负责run方法中的代码

    方式二:
        1.自定义一个类实现Runnable接口
        2.实现Runnable里面的run方法,把自定义线程的任务定义在run方法里面
        3.创建Runnable实现类对象
        4.创建Thread对象,并且把Runnable实现类对象作为参数传递
        5.调用Thread对象的start方法开启一个线程

    推荐使用:第二种 实现Runnable接口的方式;因为java单继承,多实现

    问题4:请问Runnable实现对象是线程对象吗?
      答:Runnable实现类对象并不是一个线程对象,只不过实现了Runnable接口的对象而已,只有Thread或者Thread的子类才是线程对象
    问题5:为啥要把Runnable实现类对象作为参数传递给Thread对象呢?作用是什么?
      答:作用就是把Runnable实现类对象的run方法作为线程的任务代码去执行了.

    1.7、实例

     1 package com.zn.thread;
     2 
     3 /**
     4  * @author DSHORE / 2018-5-3
     5  * 方式1的实例
     6  */
     7 /*创建多线程的方式
     8  *     方式一:
     9  *      1.自定义一个类继承Thread类
    10  *      2.重写Thread的类里面的run方法,自定义线程的代码写在run方法里面
    11  *     3.创建Thread的子类对象,并且调用start方法开启线程.
    12  *
    13  *     注意:一个线程一旦开启,那么这个线程就会执行run方法中的代码,run方法千万不要直接调用,如果直接调用它就相当于一个普通方法,就不会开启新的线程.
    14  * 
    15  * */
    16 public class Demo1 extends Thread {//1.自定义一个类继承Thread类
    17     @Override
    18     public void run() {//2.重写Thread的类里面的run方法,自定义线程的代码写在run方法里面
    19         for(int i=0;i<100;i++){
    20             System.out.println("自定义线程"+i);
    21         }
    22     }
    23     public static void main(String[] args) {
    24         Demo1 d=new Demo1();//3.创建Thread的子类对象,并且调用start方法开启线程.
    25         d.start();
    26         
    27         for(int i=0;i<100;i++){
    28             System.out.println("主线程"+i);
    29         }
    30     }
    31 }

    运行结果图: 注:每次的运行结果都不一样,线程问题

     1 package com.zn.thread;
     2 
     3 /**
     4  * @author DSHORE / 2018-5-3
     5  * 方式2的实例
     6  */
     7  /*方式二:
     8  *  1.自定义一个类实现Runnable接口
     9  *  2.实现Runnable里面的run方法,把自定义线程的任务定义在run方法里面
    10  *  3.创建Runnable实现类对象
    11  *  4.创建Thread对象,并且把Runnable实现类对象作为参数传递
    12  *  5.调用Thread对象的start方法开启一个线程
    13  *
    14  * 推荐使用:第二种 实现Runnable接口的方式;因为java单继承,多实现
    15  */
    16 class Test implements Runnable{
    17 
    18     @Override
    19     public void run() {
    20         
    21         for(int i=0;i<100;i++){
    22             System.out.println(Thread.currentThread().getName()+i);
    23         }
    24     }    
    25 }
    26 public class Demo2 {
    27     
    28     public static void main(String[] args) {
    29         //创建Runnable实现类对象
    30         Test t=new Test();
    31         //创建Thread对象,并且把Runnable实现类对象作为参数传递
    32         Thread d=new Thread(t,"二狗子");
    33         //调用Thread对象的start方法开启一个线程
    34         d.start();
    35         for (int i = 0; i <100; i++) {
    36             System.out.println(Thread.currentThread().getName()+i);
    37         }
    38     }
    39 }

    运行结果图

     1.8、线程的生命周期 图解

     

    线程生命周期的五种状态:

        1、新建(new Thread):当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

        2、就绪(runnable)当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

        3、运行(running)如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

        4、堵塞(blocked)当处于运行状态的线程失去所占用资源之后(正在运行的线程让出CPU并暂停自己的执行),便进入阻塞状态

        5、死亡(dead)线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

    线程生命周期详解:https://www.cnblogs.com/sunddenly/p/4106562.html  或  https://blog.csdn.net/pange1991/article/details/53860651

    下面是:wait()、notify()、notifyAll()、interrupt()、setDaemon()、join() 等方法的使用

     1 package com.zn.thread;
     2 
     3 /*
     4  * 线程的通讯:一个线程完成了自己的任务时,要通知另一个线程去完成另一个任务
     5  * 
     6  * 生产者与消费者
     7  * 
     8  * wait():等待。如果线程执行到了wait方法,那么该线程会进入等待状态,等待状态下的线程必须要被其他线程调用notify()方法才能唤醒.
     9  * notify():唤醒。唤醒线程池等待的其中一条线程
    10  * notifyAll(): 唤醒线程池中所等待的线程
    11  * */
    12 //产品
    13 class Product{
    14     String name;//产品名字
    15     double price;//价格
    16     boolean flag=false;//生产者是否生产完成的标志,默认是没有生产完成    
    17 }
    18 //生产者
    19 class Producer extends Thread{    
    20     Product p;
    21     public  Producer(Product p) {
    22         this.p=p;
    23     }
    24     @Override
    25     public void run() {
    26         int i=0;
    27         while(true){
    28             synchronized (p) {
    29                 if(p.flag==false){
    30                     if(i%2==0){
    31                         p.name="苹果";
    32                         p.price=5.0;
    33                     }else{
    34                         p.name="香蕉";
    35                         p.price=3.5;
    36                     }
    37                     System.out.println("生存者生产出了:"+p.name+"; 价格是:"+p.price);
    38                     p.flag=true;//flag为true时,通知消费者去消费
    39                     i++;
    40                     p.notify();//唤醒 消费者去消费
    41                 }else{
    42                     try {
    43                         p.wait();//生产者等待
    44                     } catch (InterruptedException e) {
    45                         e.printStackTrace();//打印异常信息
    46                     }
    47                 }
    48             }
    49         }
    50     }
    51 }
    52 //消费者
    53 class Customer extends Thread{
    54     Product p;
    55     public Customer(Product p) {
    56         this.p=p;
    57     }
    58     @Override
    59     public void run() {
    60         while(true){
    61             synchronized (p) {    
    62                 if(p.flag==true){                
    63                     System.out.println("消费者消费了:"+p.name+"; 价格:"+p.price);            
    64                     p.flag=false;//flag为false时,通知生产者去生产
    65                     p.notify();//唤醒 生产者去生产
    66                 }else{
    67                     try {
    68                         p.wait();//消费者也等待了
    69                     } catch (InterruptedException e) {
    70                         e.printStackTrace();//打印异常信息
    71                     }
    72                 }
    73             }
    74         }
    75     }
    76 }
    77 public class Demo5 {
    78     public static void main(String[] args) {
    79         Product p=new Product();
    80         //创建线程对象
    81         Producer producer=new Producer(p);
    82         Customer customer=new Customer(p);
    83         //开启线程
    84         producer.start();
    85         customer.start();
    86     }
    87 }
     1 package com.zn.thread;
     2 /*停止线程:
     3  * 1.停止一个线程 我们一般都会通过一个变量去控制
     4  * 2.如果需要停止一个处于等待状态的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用
     5  * */
     6 public class Demo2 extends Thread{
     7     boolean flag=true;
     8     public Demo2(String name) {
     9         super(name);
    10     }
    11     @Override
    12     public synchronized void run() {
    13         int i=0;
    14         while(flag){
    15             System.out.println("嘿,你好"+Thread.currentThread().getName()+":"+i);    
    16             i++;
    17         }                
    18     }
    19     public static void main(String[] args) {
    20         Demo2 d=new Demo2("狗娃");
    21         d.start();
    22         System.out.println("1111");
    23         for (int i = 0; i <100; i++) {
    24             d.flag=false;
    25             d.interrupt(); //interrupt():调用该方法后,该线程被置于"中断状态"       
    26         }
    27     }    
    28 }
     1 package com.zn.thread;
     2 /*
     3  * 保护线程(后台线程):在一个进程中如果只剩下了守护线程,那么守护线程也会死亡
     4  * 需求:模拟QQ下载更新包
     5  * 
     6  * */
     7 public class Demo3 extends Thread{
     8     public Demo3(String name){
     9         super(name);
    10     }    
    11     @Override
    12     public void run() {
    13         for (int i = 1; i <=100; i++) {
    14             System.out.println("更新包目前下载"+i+"%");
    15             if(i==100){
    16                 System.out.println("下载完毕");
    17             }
    18         }
    19         try {
    20             Thread.sleep(100);
    21         } catch (InterruptedException e) {
    22             // TODO Auto-generated catch block
    23             e.printStackTrace();
    24         }
    25     }
    26     public static void main(String[] args) {
    27         Demo3 d=new Demo3("后台线程");
    28             d.setDaemon(true);//将该线程标记为守护线程或用户线程,该方法必须在启动线程前调用
    29             System.out.println(d.isDaemon());
    30             d.start();
    31         for (int i = 1; i <=100; i++) {
    32             System.out.println(Thread.currentThread().getName()+":"+i);
    33         }
    34     }
    35 }

    join()  方法

     1 package com.zn.thread;
     2 
     3 /**
     4  * @author DSHORE / 2018-5-14
     5  *
     6  */
     7 /*
     8  * join()方法:加入
     9  *
    10  * 需求:在没加油处插入去买酱油的过程,顺序要正确
    11  *
    12  * */
    13 
    14 //老妈
    15 class Mon extends Thread{
    16     @Override
    17     public void run() {
    18         System.out.println("妈妈洗菜");
    19         System.out.println("妈妈切菜");
    20         System.out.println("妈妈准备炒菜,发现没酱油了");
    21         Son s=new Son();
    22         s.start();
    23         try {
    24             s.join();//加入,一个线程如果执行join语句,那么就会有新的线程加入,执行该语句的线程必须要让步给新加入的线程完成任务,然后才能继续执行
    25         } catch (InterruptedException e) {
    26             e.printStackTrace();
    27         }
    28         System.out.println("妈妈继续炒菜");
    29         System.out.println("全家一起卡饭...");
    30     }
    31 }
    32 //儿子
    33 class Son extends Thread{
    34     @Override
    35     public void run() {
    36         System.out.println("让儿子下楼去买");
    37         System.out.println("儿子买完酱油");
    38         System.out.println("上楼把酱油给老妈");
    39     }    
    40 }
    41 public class Demo8 {
    42     public static void main(String[] args) {        
    43         Mon m=new Mon();
    44         m.start();
    45     }    
    46 }

    运行结果图

              

    原创作者:DSHORE

    作者主页:http://www.cnblogs.com/dshore123/

    原文出自:http://www.cnblogs.com/dshore123/p/8984060.html

    欢迎转载,转载务必说明出处。(如果本文对您有帮助,可以点击一下右下角的 推荐,或评论,谢谢!

  • 相关阅读:
    LeetCode:387字符串中唯一出现一一次的字符
    LeetCode-79. 单词搜索
    LeetCode-75. 颜色分类
    LeetCode-121. 买卖股票的最佳时机
    LeetCode-58. 最后一个单词的长度
    LeetCode-1103. 分糖果 II
    LeetCode:283. 移动零
    LeetCode:38. 外观数列
    LeetCode:70. 爬楼梯
    获取美拍视频的链接--JS分析
  • 原文地址:https://www.cnblogs.com/dshore123/p/8984060.html
Copyright © 2011-2022 走看看