zoukankan      html  css  js  c++  java
  • java ---线程池和多线程

     线程池

    线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

      使用线程池方式--Runnable接口

    使用线程池中线程对象的步骤:

    1.  创建线程池对象

    2.  创建Runnable接口子类对象

    3.  提交Runnable接口子类对象

    4.  关闭线程池

    //    代码演示:
    public class ThreadPoolDemo {
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
            //创建Runnable实例对象
            MyRunnable r = new MyRunnable();
            
            //自己创建线程对象的方式
            //Thread t = new Thread(r);
            //t.start(); ---> 调用MyRunnable中的run()
            
            //从线程池中获取线程对象,然后调用MyRunnable中的run()
            service.submit(r);
            //再获取个线程对象,调用MyRunnable中的run()
            service.submit(r);
            service.submit(r);
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    
    //关闭线程池
            //service.shutdown();
        }
    }
    
    //    Runnable接口实现类
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("我要一个教练");
            
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " +Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
        }
    }

      使用线程池方式—Callable接口

      使用线程池中线程对象的步骤:

    1.  创建线程池对象

    2.  创建Callable接口子类对象

    3.  提交Callable接口子类对象

    4.  关闭线程池

    //代码演示:
    public class ThreadPoolDemo {
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
            //创建Callable对象
            MyCallable c = new MyCallable();
            
            //从线程池中获取线程对象,然后调用MyRunnable中的run()
            service.submit(c);
            
            //再获取个教练
            service.submit(c);
            service.submit(c);
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    
    //关闭线程池
            //service.shutdown();
        }
    }
    
    //    Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
    public class MyCallable implements Callable {
        @Override
        public Object call() throws Exception {
            System.out.println("我要一个教练:call");
            Thread.sleep(2000);
            System.out.println("教练来了: " +Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
            return null;
        }
    }

      线程池练习:返回两个数相加的结果

    要求:通过线程池中的线程对象,使用Callable接口完成两个数求和操作

      Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

      get() 获取Future对象中封装的数据结果

    //代码演示:
    public class ThreadPoolDemo {
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            //创建线程池对象
            ExecutorService threadPool = Executors.newFixedThreadPool(2);
            
            //创建一个Callable接口子类对象
            //MyCallable c = new MyCallable();
            MyCallable c = new MyCallable(100, 200);
            MyCallable c2 = new MyCallable(10, 20);
            
            //获取线程池中的线程,调用Callable接口子类对象中的call()方法, 完成求和操作
            //<Integer> Future<Integer> submit(Callable<Integer> task)
            // Future 结果对象
            Future<Integer> result = threadPool.submit(c);
            //此 Future 的 get 方法所返回的结果类型
            Integer sum = result.get();
            System.out.println("sum=" + sum);
            
            //再演示
            result = threadPool.submit(c2);
            sum = result.get();
            System.out.println("sum=" + sum);
            //关闭线程池(可以不关闭)
            
        }
    }
    //    Callable接口实现类
    public class MyCallable implements Callable<Integer> {
        //成员变量
        int x = 5;
        int y = 3;
        //构造方法
        public MyCallable(){
        }
        public MyCallable(int x, int y){
            this.x = x;
            this.y = y;
        }
    
        @Override
        public Integer call() throws Exception {
            return x+y;
        }
    }

     多线程

     线程安全

      我们通过一个案例,演示线程的安全问题:

    电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。

    我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)

    需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟

    //    测试类
    public class ThreadDemo {
        public static void main(String[] args) {
            //创建票对象
            Ticket ticket = new Ticket();
            
            //创建3个窗口
            Thread t1  = new Thread(ticket, "窗口1");
            Thread t2  = new Thread(ticket, "窗口2");
            Thread t3  = new Thread(ticket, "窗口3");
            
            t1.start();
            t2.start();
            t3.start();
        }
    }
    //    模拟票
    public class Ticket implements Runnable {
        //共100票
        int ticket = 100;
    
        @Override
        public void run() {
            //模拟卖票
            while(true){
                if (ticket > 0) {
                    //模拟选坐的操作
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
                }
            }
        }

    同步代码块

    同步代码块: 在代码块声明上 加上synchronized

    synchronized (锁对象) {
        可能会产生线程安全问题的代码
    }
    //同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    使用同步代码块,对电影院卖票案例中Ticket类进行如下代码修改:

    public class Ticket implements Runnable {
        //共100票
        int ticket = 100;
        //定义锁对象
        Object lock = new Object();
        @Override
        public void run() {
            //模拟卖票
            while(true){
                //同步代码块
                synchronized (lock){
                    if (ticket > 0) {
                        //模拟电影选坐的操作
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
                    }
                }
            }
        }
    }

      同步方法

      同步方法:在方法声明上加上synchronized

    public synchronized void method(){
           可能会产生线程安全问题的代码
    }
    //同步方法中的锁对象是 this

    使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改:

    public class Ticket implements Runnable {
        //共100票
        int ticket = 100;
        //定义锁对象
        Object lock = new Object();
        @Override
        public void run() {
            //模拟卖票
            while(true){
                //同步方法
                method();
            }
        }
    
    //同步方法,锁对象this
        public synchronized void method(){
            if (ticket > 0) {
                //模拟选坐的操作
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
            }
        }
    }

     Lock接口

    我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改:

    public class Ticket implements Runnable {
        //共100票
        int ticket = 100;
        
        //创建Lock锁对象
        Lock ck = new ReentrantLock();
        
        @Override
        public void run() {
            //模拟卖票
            while(true){
                //synchronized (lock){
                ck.lock();
                    if (ticket > 0) {
                        //模拟选坐的操作
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
                    }
                ck.unlock();
                //}
            }
        }
    }
  • 相关阅读:
    #Linux 系统管理命令 | top命令
    Linux 操作文件目录
    背包问题(1)背包的引入
    硬币找零问题
    输入一个正数 n,输出所有和为 n 连续正数序列。 java实现
    张小二求职记之 单例模式(三)之决战多线程
    张小二求职 之单例模式(一)
    张小二求职记之 单例模式(二)
    待修改 nyoj 412 又是一个遗留问题
    nyoj 904 hashmap
  • 原文地址:https://www.cnblogs.com/haoduoyu0512/p/13439670.html
Copyright © 2011-2022 走看看