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