关于多线程

1.创建多线程

1.1通过继承Thred并重写run方法来创建一个新的进程

public class MyThread extends Thread {
    
    /**
     * run方法就是线程要执行的任务方法
     */
    @Override
    public void run() {
        //这里的代码 就是一条新的执行路径
        //这个执行路径的触发方式 不是调用run方法 而是通过thread对象中的start()来启动任务
        
        for (int i = 0; i<10;i++){
            System.out.println("这是 "+i);
        }
    }
}

2.在main函数中调用 调用start()方法来创建线程

public class dome {
    public static void main(String[] args) {
        MyThread m = new MyThread();
        m.start();
        for (int i = 0; i<10;i++){
            System.out.println("那是 "+i);
        }
    }
}

多个线程并不会顺序执行,java中多线程的执行方式采用的是抢占模式:多个线程互相抢,顺序不固定

1.2创建一个接口接向Runnable,重写当中的run方法

 static class MyRunnable implements Runnable{
        @Override
        public void run() {           //获取当前正在执行的线程名字
            System.out.println(Thread.currentThread().getName());
        }
    }
Thread.currentThread().getName()方法返回的是目前执行线程的名字
public class dome1 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        //在创建线程的同时个线程取个名字
        Thread t = new Thread(new MyRunnable(),"锄禾日当午");
        t.start();
    }

3.创建一个接口接向callable,重写当中的call方法

static class MyCallable implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
               使用包装类
               这里写需要执行的代码
           
            }
            return 100;这个是有返回值的线程
        }
    }

public static void main(String[] args) {
        main方法中这样写来启动线程
        Callable<Integer> c =new MyCallable() ;
        FutureTask<Integer> task = new FutureTask<>(c);
        new Thread(task).start();//启动线程

        task.get()//调用这个方法会让子线程全部执行完毕之后
                  //在执行主线程
}

2.线程休眠

Thred.sleep(毫秒为单位)使用这个方法会让程序在执行是暂停程序的执行,例如以下代码,1000毫秒 = 1,秒,意思就是每当程序走到第6行时,程序会暂停一秒钟后继续执行,这段代码体现出来的效果就是每秒打印一次数字,由于这个方法有异常,所以需要手动处理一下。

public class sleep {
    public static void main(String[] args){
        for (int i = 0; i < 10; i++){
            System.out.println(i);
            try {
                Thread.sleep(1000);//以毫秒为单位 进行线程的休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.守护线程

守护用户线程,当最后一个用户线程结束时,守护线程自动结束。

main为用户线程 当main函数线程结束时,守护线程同时也会结束,

使用:线程名.setDaemon(ture)方法来将该线程设置为守护线程。

例如main中执行五次循环,子线程要执行10次,当子线程变成了守护线程,那么main循环五次结束后,子线程也跟着结束,即使当时的循环还没有结束

4.线程安全

定义一个买票方法,假设有10张票 循环卖,当票数为0时退出循环,卖完了。同时创建三个进程一起买票。理论上count=0时循环将被结束,也就是不可能会打印出负数来,可现在这段代码运行的结果就是有可能会打印出负数,这样显然是有问题的,这就是线程不安全问题,那么为了解决这个问题就得给线程加锁。

public static void main(String[] args) {
        
        Runnable run = new Ticket();
        new Thread(run).start();//创建线程1
        new Thread(run).start();//创建线程2
        new Thread(run).start();//创建线程3
    }

static class Ticket implements Runnable {
        //票数
        private int count = 10;
        @Override
        public void run() {
            while (true) {
                   if (count > 0) {
                        //卖票
                        System.out.println("正在准备买票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(
                        Thread.currentThread().getName() 
                        + "出票成功,余票" + count);
                   }else{
                       break;
                   }
            }
        }

    }

1 同步代码块

创建一个object对象成为锁对象,利用synchronized来实现锁住,这样子多条线程进入之后就会派对执行


Object o = new Object()
synchronized(o){
        这里的线程就会排队执行
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备买票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName() 
                        + "出票成功,余票" + count);
                        }else{
                       break;
                    }
         }

2.同步方法

将可能出现线程安全的代码打包成方法 并用synchronized关键字修饰这个方法,这里面的方法也会派对执行

3.显示所

//ture 表示公平锁 加了之后就会按照顺序执行线程
//不加是不公平锁 所有线程抢着执行
Lock lock = new ReentrantLock(true);

lock.lock();

这之中的代码就会排队执行
lock.unlock();

5.生产者消费者问题

假设有生产者消费者两个线程,在main函数中同时开启,我们要求的是生产者生产一次 消费者·消费一次 但是因为java的原因 有多个线程抢着执行,导致生产者还没生产 消费者就已经开始消费了,或者说消费者还没开始消费,生产者一直再生产,

为了解决这个问题 采取的方法时 当生产者在生产时,消费者睡过去 当生产者生产完了自己喊起消费者消费,同时自己睡过去,当消费者消费完时,喊起生产者生产,同时自己睡过去,以此类推,这样就不会出现两个抢占线程的问题

public class cook {
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
       new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;

        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++){
                if (i%2 ==0){
                    f.setNameAndTaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务员
    static class Waiter extends Thread{
        private Food f;

        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;//名字
        private String taste;//味道

        //true表示可以生产
        private boolean flag = true;

       public synchronized void setNameAndTaste(String name,String taste) {
           if (flag) {
               this.name = name;
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               this.taste = taste;
               flag = false;
               this.notifyAll();//唤醒所有的线程
               try {
                   this.wait();//该线程睡过去
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }

       public synchronized void get(){
           if (!flag) {
               System.out.println("服务员端走的菜名:是" + name + ",味道:" + taste);
               flag = true;
               this.notifyAll();
               try {
                   this.wait();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }
    }
}

5.线程的六种状态

1.new:尚未启动状态

2.Runnable:正在执行状态

3.Blocked:当一个线程正在排队时的状态

4.Wating:线程被休眠处于无线等待的状态

5.TimeWating:等待被唤醒的状态

6.terminated:结束状态

6.线程池

1.缓存线程池

同时创建一定个数个线程 之后需要运行的线程都使用之前创建过的线程,当线程个数超出创建后的会自动扩容

 public static void main(String[] args) {
        创建一个缓存线程池
        ExecutorService service = Executors.newCachedThreadPool();
        //指挥向线程池中执行新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });


    }

2.定长线程池

在创建时就固定好长度,多个线程一起执行,得等待有空闲线程才能执行

 /**
     * 定长线程池 长度是指定的数值
     * 任务加入之后的执行流程
     *  1.判断线程池是否存在空线程
     *  2.存在则使用
     *  3.不存在空线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
     *  4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空现场
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }

3.单线程池

 /**
     * 单线池
     * 执行流程
     * 1.判断线程池的那个线程是否空闲
     * 2.空闲则使用 不空闲则等待空闲后使用
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"撒大声地");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"撒大声地");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"撒大声地");
            }
        });
    }

5.周期任务 定长线程池

/**
     * 周期任务 定长线程池
     * 执行流程
     *  1.判断线程池是否存在空线程
     *      *  2.存在则使用
     *      *  3.不存在空线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
     *      *  4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空现场
     */
    public static void main(String[] args) {
        //创建周期定长线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 定时执行一次
         * 参数1.定时执行的任务
         * 参数2.时长数字
         * 参数3.时长数字的单位 TimeUnit的常量指定
        */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        },5, TimeUnit.SECONDS);
         


        /**
         * 周期执行任务
         * 参数1.任务
         * 参数2.延迟时长数字 第一次执行在什么时间以后
         * 参数3.周期时长数字 每隔多久执行一次
         * 参数4.时长数字的单位
         *
         */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("大八嘎");
            }
        },5,1,TimeUnit.SECONDS);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值