java16 程序、进程、线程
一、程序、进程、线程 1.程序:程序就是我们编写的代码,也就是指令集,是一个静态概念。 2.进程:操作系统调度我们的程序,动态概念,一个程序就是一个进程。进程是程序的一次动态执行过程,占用特定的地址空间,每个进程是独立的,由3部分组成:cpu,data(数据),code(代码区)。缺点:内存浪费,cpu会有负担。 3.线程:是进程中的一个单一的执行路径,线程又被称为轻量级的进程。一个进程可以拥有多个并行的线程。一个进程中的线程共享内存单元/内存地址空间,多个线程可以共享数据,可以访问相同的变量和对象,是从同一堆中分配对象。所以有一个并发的问题,一份数据多个线程来用。由于线程间的通信是在同一地址空间进行,所以不需要额外的通信机制, 一个程序就有一个进程,每个进程有独立的代码和数据空间,进程间的切换有较大的开销。 多进程是操作系统能同时运行多个任务(程序),多线程是在同一应用程序中有多个执行流程。 系统为每个进程分配不同的内存区域,不会为线程分配内存(因为线程在进程中),线程是进程的一部分,所以线程也被成为轻量级进程。 线程在java中也是类和接口,进程包含线程,线程是进程的一部分,线程是不同的执行路径, 多线程就是有多核,多个cpu。cpu有一个调度,有一个时间片,时间片非常的小。main方法是一个主线程,就是一条路径。 线程创建的第一种方式: /** * 模拟龟兔赛跑 1、创建多线程 继承 Thread +重写run(线程体) 2、使用线程: 创建子类对象 + 对象.start() 线程启动 */ public class Rabbit extends Thread { @Override public void run() {//线程体,一切从run开始就像main方法一切从main开始,这个运行一切从run开始。 for(int i=0;i<100;i++){ System.out.println("兔子跑了"+i+"步"); } } } class Tortoise extends Thread { @Override public void run() { //线程体 for(int i=0;i<100;i++){ System.out.println("乌龟跑了"+i+"步"); } } } public class RabbitApp { public static void main(String[] args) {//main也是一个线程有自己的路径。 //创建子类对象 Rabbit rab = new Rabbit(); Tortoise tor =new Tortoise(); //调用start 方法,内部由cpu调用他们,start()只是加到线程里面。 rab.start(); //rab.run();//不要调用run方法,调用run方法不是启动线程而是普通方法的调用。程序会从上到下依次调用执行。 tor.start(); //tor.run(); for(int i=0;i<100;i++){ System.out.println("main==>"+i); } /*main==>96 main==>97 main==>98 main==>99 乌龟跑了0步 乌龟跑了1步 乌龟跑了2步 兔子跑了14步 兔子跑了15步 兔子跑了16步 兔子跑了17步*/ }//rab.start(); 和 tor.start();和 main()的for(int i=0;i<100;i++){System.out.println("main==>"+i);}3者交替执行 } 第二种实现多线程的方式: 继承Thread类方式的缺点是如果一个类已经从另一个类继承则无法再继承Thread类。 所以通过实现Runnable接口,有点是可以实现同时继承。 Runnable接口用到了静态代理设计模式,下面先看看静态代理设计模式。 /** * 静态代理 设计模式(静态代理的时候WeddingCompany是写死的,动态代理的时候WeddingCompany是动态创建的,也就是说代理对象是动态的)(代理就是代别人做事,被代理就是别人做事都是做的自己的事) * 代理的要求: * 1、真实角色 * 2、代理角色: 持有真实角色的引用 * 3、二者 实现相同的接口(代理去实现这个方法也就是被代理的去实现这个方法) */ public class StaticProxy { public static void main(String[] args) { //创建真实角色 Marry you =new You(); //创建代理角色 +真实角色的引用 WeddingCompany company =new WeddingCompany(you); //执行任务 company.marry(); } } //接口 interface Marry{ void marry();//相当于public abstract void marry();接口里面的方法都是公共的抽象的 } //真实角色 class You implements Marry{ @Override public void marry() { System.out.println("you and 嫦娥结婚了...."); } } //代理角色 class WeddingCompany implements Marry{ private Marry you; public WeddingCompany() { } public WeddingCompany(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(); } } 静态代理 + Runnable接口实现多线程: /** 推荐 Runnable 创建线程 1)、避免单继承的局限性(继承接口后还可以继承其他类) 2)、便于共享资源 使用 Runnable 创建线程 1、类 实现 Runnable接口 +重写 run() --> 真实角色类 2、启动多线程 使用静态代理 1)、创建真实角色 2)、创建代理角色 +真实角色引用 3)、调用 .start() 启动线程 */ public class Programmer implements Runnable { @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("一边敲helloworld...."); } } } public class ProgrammerApp { public static void main(String[] args) {//main也是一个线程有自己的路径。 //1)、创建真实角色 Programmer pro =new Programmer(); //2)、创建代理角色 +真实角色引用 Thread proxy =new Thread(pro);//Thread和Programmer一样实现了Runnable接口,proxy代理别人做事,所以proxy是代理,Programmer是被代理。所以这里用到了静态代理模式。 //3)、调用 .start() 启动线程 proxy.start(); for(int i=0;i<1000;i++){ System.out.println("一边聊qq...."); } } } /** * 方便共享资源 */ public class Web12306 implements Runnable { private int num =10;//50张票 @Override public void run() { while(true){ if(num<=0){ break; //跳出循环 } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } public static void main(String[] args) {//这个线程的名字是main //真实角色 Web12306 web = new Web12306(); //public class Thread implements Runnable {} //静态代理模式,Thread和Web12306都实现了Runnable接口, // public Thread(Runnable target, String name) {} Thread t1 =new Thread(web,"路人甲");//线程t1的名字 Thread t2 =new Thread(web,"黄牛已");//线程t2的名字 Thread t3 =new Thread(web,"攻城师");//线程t3的名字 //启动线程 t1.start();//start()就启动了线程 t2.start(); t3.start(); /*路人甲抢到了9 黄牛已抢到了10 黄牛已抢到了8 黄牛已抢到了7 黄牛已抢到了6 黄牛已抢到了5 黄牛已抢到了3 黄牛已抢到了2 黄牛已抢到了1 攻城师抢到了4*/ } } 线程: 一、继承Thread,重写run()方法,启动:创建子类对象+对象.start() 二、实现Runnable,重写run(),启动:使用静态代理,1.创建真实角色,2.创建代理角色(Thread),3.代理角色.start() 实现Runnable,重写run()的缺点是不能拋异常,没有返回值,因此使用Callable接口解决这个问题。使用Callable创建线程比较繁琐,优点是又返回值并且可以对外申明异常。 通过Callable接口实现多线程 优点:可以获取返回值 Callable和Future接口,Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其他线程执行的任务。 Callable和Runnable有几点不同: 1.Callable规定的方法是call(),而Runnable规定的方法是run() 2.call()可抛出异常,而run()是不能抛出异常的 3.Callable的任务执行后可返回值,运行Callable任务可以拿到一个Future对象,而Runnable任务是不能返回值的,Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行的情况,可取消任务的执行,还可以获取任务的执行结果。 缺点:繁琐 步骤: 1.创建Callable实现类,重写call方法。 2.借助执行调度服务ExecutorService获取Future对象。 ExecutorService ser=Executors.newFixedThreadPool(2); Future result =ser.submit(实现类对象) ; 3.获取值result.get(); 4.停止服务ser.shutdownNow(); import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 使用Callable创建线程 */ public class Call { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建2个线程 ExecutorService ser=Executors.newFixedThreadPool(2); Race tortoise = new Race("老不死",1000); Future<Integer> result1 =ser.submit(tortoise) ;//让线程执行tortoise,获取值, tortoise.setFlag(false); //停止线程体循环 int num1 =result1.get();//进入call方法 System.out.println("乌龟跑了-->"+num1+"步"); Race rabbit = new Race("小兔子",500); Future<Integer> result2 =ser.submit(rabbit) ; rabbit.setFlag(false); int num2 =result2.get(); System.out.println("小兔子跑了-->"+num2+"步"); Thread.sleep(2000); //2秒 ser.shutdownNow();//停止服务 } } class Race implements Callable<Integer>{//继承Callable接口,重写call方法。 private String name ; //名称 private long time; //延时时间 private boolean flag =true; private int step =0; //步 public Race() { } public Race(String name) { super(); this.name = name; } public Race(String name,long time) { super(); this.name = name; this.time =time; } @Override public Integer call() throws Exception {//有返回值,并且可以申明异常。 while(flag){ Thread.sleep(time); //延时 step++; } return step; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getStep() { return step; } public void setStep(int step) { this.step = step; } } 线程的状态: 1.线程创建。 2.调用start()方法,线程进入就绪状态。 3.cpu调度,线程进入运行状态。 4.任务完成,线程终止。 5.线程运行过程中,出现导致阻塞的事件,线程进入阻塞状态,阻塞解除后,线程又进入就绪状态。(阻塞接触后是进入就绪状态不是运行状态)。 线程干涉: 线程体通过标示符,外部通过条件调用。 public class StopDemo01 { public static void main(String[] args) { Study s =new Study(); new Thread(s).start(); //外部干涉 for(int i=0;i<100;i++){ if(50==i){ //外部干涉 s.stop(); } System.out.println("main.....-->"+i); } } } class Study implements Runnable{ //1)、线程类中 定义 线程体使用的标识 private boolean flag =true; @Override public void run() { //2)、线程体使用该标识 while(flag){ System.out.println("study thread...."); } } //3)、对外提供方法改变标识 public void stop(){ this.flag =false; } } 阻塞: 1.join:合并线程。 2.yeld:暂停自己,让别的线程执行。 3.sleep:休眠,休眠的时候不会释放锁。常用于与时间相关的和模拟网络延时。 /** * join:合并线程 */ public class JoinDemo01 extends Thread { public static void main(String[] args) throws InterruptedException { JoinDemo01 demo = new JoinDemo01(); Thread t = new Thread(demo); //新生 t.start();//就绪 //cpu调度 运行 for(int i=0;i<1000;i++){ if(50==i){ t.join(); //main阻塞...一定要等到t的线程执行完main才能开始执行。 } System.out.println("main...."+i); } } @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("join...."+i); } } } public class YieldDemo01 extends Thread { public static void main(String[] args) { YieldDemo01 demo = new YieldDemo01(); Thread t = new Thread(demo); //新生 t.start();//就绪 //cpu调度 运行 for(int i=0;i<1000;i++){ if(i%20==0){ //暂停本线程 main,yield是一个静态方法,写在哪个线程体里面就暂停谁。之后什么时候再次调用是由cpu的调度算法决定的。 Thread.yield(); } System.out.println("main...."+i); } } @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("yield...."+i); } } } /** * 倒计时 * 1、倒数10个数,一秒内打印一个 * 2、倒计时 */ public class SleepDemo01 { public static void main(String[] args) throws InterruptedException { Date endTime =new Date(System.currentTimeMillis()+10*1000);//当前时间加上10秒 long end =endTime.getTime(); while(true){ //输出 System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //等待一秒 Thread.sleep(1000); //构建下一秒时间 endTime =new Date(endTime.getTime()-1000); //10秒以内 继续 否则 退出 if(end-10000>endTime.getTime()){ break; } } /*17:56 17:55 17:54 17:53 17:52 17:51 17:50 17:49 17:48 17:47 17:46*/ } public static void test1() throws InterruptedException{ int num =10; while(true){ System.out.println(num--); Thread.sleep(1000); //暂停 if(num<=0){ break; } } } } /** * Sleep模拟 网络延时 线程不安全的类 */ public class SleepDemo02 { public static void main(String[] args) { //真实角色 Web12306 web= new Web12306(); Web12306 web2 = new Web12306(); //3个代理同时操控一个被代理对象, Thread t1 =new Thread(web,"路人甲"); Thread t2 =new Thread(web,"黄牛已"); Thread t3 =new Thread(web,"攻城师"); //启动线程 t1.start(); t2.start(); t3.start(); } } class Web12306 implements Runnable { private int num =50; @Override public void run() { while(true){ if(num<=0){//num=1时,当a、b、c依次进来后依次开始睡眠。 break; //跳出循环 } try { Thread.sleep(5000);///sleep也是暂停线程的一种方式.休眠的时候不会释放锁, } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }//a醒了以后带走了1,b醒了以后带走了0,c醒了以后带走了-1。 } }