zoukankan      html  css  js  c++  java
  • 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。
        }
    }
  • 相关阅读:
    js字符串String常用方法
    flexible.js结合rem实现移动端自适应布局
    Django API验证(令牌)
    错误堆栈信息
    JS Ajax异步请求发送列表数据后面多了[]
    根据后端传的时间前端js进行倒计时
    Vue select 下拉菜单
    centos 6.x 部署uwsgi+flask项目
    css 让背景图片不停旋转
    supervisor管理uwsgi
  • 原文地址:https://www.cnblogs.com/yaowen/p/4833629.html
Copyright © 2011-2022 走看看