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

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

原文地址:https://www.cnblogs.com/dshore123/p/8984060.html