Java中多线程和自定义线程池

public class Threads {

    volatile int num;
    int num2;

    @Test
    void test() throws InterruptedException {

        /*开启一个新线程最简单的方法,继承Thread类,重写run方法*/
        new Thread(){
            @Override
            public void run() {
                System.out.println("New Thread");
            }
            /*调用start(),启动线程*/
        }.start();

        /*实现Runnable接口*/
        Thread t2 = new Thread(() -> System.out.println("New Runnable"));
        t2.start();

        /*实现Callable接口
        * 由于Callable接口的call方法是有返回值的
        * 这里可以使用FutureTask来接收返回值*/
        FutureTask<String> ft = new FutureTask(() -> "New Callable");
        Thread t3 = new Thread(ft);
        t3.start();
        try {
            /*当调用FutureTask的get方法时,当前线程会进入等待状态
            * 直到获取到get方法的返回值后才继续运行*/
            System.out.println(ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        /*线程安全*/
        /*当一个变量被多个线程访问时,即变量被线程1做修改操作时,线程2又访问了进来,线程1完成了操作变量值已被修改
        * 但线程2访问到的仍是未被修改前的值,这样就出现了线程不同步问题
        * 解决方案 共享变量使用volatile关键字修饰,他可以保证当变量发生改变时对所有线程都是立即可见的
        * 但他虽然能保证变量的原子性,但会阻止编译器对代码的优化而降低效率,所以一般使用synchronized来代替volatile
        * 如下示例,两个线程一起打印0-99的整数*/

        new Thread(() -> {
            while (num<100) {
                System.out.println(num++);
            }
        }).start();

        new Thread(() -> {
            while (num<100){
                System.out.println("----"+num++);
            }
        }).start();

        /*方案2:使用synchronized关键字*/
        final Object o = new Object();
        new Thread(() -> {
            while (num2<100){
                /*使用时要传递一个引用数据类型实例来当做“锁”,当该锁被X线程拿到时,其他线程会进入等待状态,
                 * 只有X线程执行完锁中的代码时才会释放锁,此时其他线程及X线程才会再次争夺锁*/

                synchronized (o){
                    System.out.println(num2++);
                }
            }
        }).start();

        new Thread(() -> {
            while (num2<100){
                synchronized (o){
                    try {
                        /*使用sleep方法使当前线程进入睡眠状态,参数单位:毫秒*/
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println((num2++)+"-----------------");
                }
            }
        }).start();
        /*同样,可以用synchronized来修饰一个方法,当此方法被X线程访问时,其他线程要想访问此方法就必须等待X线程的访问结束
        * 应用实例*/
        class AThread{
            //在线程中不允许非final修饰的局部基本类型变量出现
            int count = 0;
            int check = 0;
        }
        AThread a = new AThread();

        new Thread(() -> {
            while (true){
                synchronized (o){
                    /*当count已经不低于10的时候此时的线程已经拿到了锁,此时应该结束循环,
                    但仍会将锁中的逻辑再次执行一遍。所以这个控制循环结束的条件
                    * 应在拿到锁之后判断*/
                    if(a.count>=10){
                        return;
                    }
                    while(a.check%3==0){
                        System.out.println("a");
                        a.check++;
                    }

                }
            }
        }).start();

        new Thread(() -> {
            while (true){
                synchronized (o){
                    if(a.count>=10){
                        return;
                    }
                    while (a.check%3==1){
                        System.out.println("b");
                        a.check++;
                    }
                }
            }
        }).start();

        new Thread(() -> {
            while (true){
                synchronized (o){
                    if(a.count>=10){
                        return;
                    }
                    while(a.check%3==2){
                        System.out.println("c");
                        a.check++;
                        a.count++;
                    }
                }
            }
        }).start();

        /*wait(),notify(),notifyAll()
        * wait():当前线程调用此方法会立即将锁释放,并且使当前线程进入等待状态,只有当被其他线程唤醒
        * 时才会进入就绪队列。否则,此线程将被一直挂起
        * notify():当某个线程调用此方法时会从等待队列中取出一个线程
        * notifyAll():类似于notify(),会释放等待队列中的所有线程
        * 这些方法要声明在synchronized中,因为操作的是锁,若当前线程未持有锁则会抛出相关异常*/
        /*应用实例:两个线程交替打印0-100的整数*/
        a.count = 0;
        Thread thread = new Thread(() -> {
            while (true){
                synchronized (o){
                    if (a.count>100){
                        return;
                    }
                    /*current():获得当前线程的实例
                     getName():获得当前线程名称,如果未被设置则使用默认值
                    设置X线程名称,X.setName(String name)*/
                    System.out.println(Thread.currentThread().getName()+":"+a.count++);
                    //*取出一个被挂起的线程,此操作不会释放锁,只是让被取出的线程进入就绪状态*//*
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        new Thread(thread).start();
        new Thread(thread).start();


        /*JDK 5中提供的"锁"*/
        /*可以使用默认的无参构造器构造一个Lock实例,可以传递一个公平性参数(true or false)
        * 此参数设置的公平性是指:当一个线程本次使用锁完成执行后,该线程下次便不再参与锁的争夺*/
        Lock lock = new ReentrantLock(/*公平性参数默认为false*/);
        /*此时这个锁就是一个“公平锁”,利用这一特性同样可以完成以上的交替打印操作*/
        final Lock lock1 = new ReentrantLock(true);
        /*使用示例*/
        a.count = 0;
        Thread thread1 = new Thread(() -> {
            while (true){
                /*上锁*/
                lock1.lock();
                if(a.count>100){
                    return;
                }
                System.out.println(Thread.currentThread().getName()+":"+a.count++);
                /*解锁*/
                lock1.unlock();
            }
        });
        new Thread(thread1).start();
        new Thread(thread1).start();
        /*另外,在提一点,在多线程同步问题中,一定要确保各个线程拿到和操作的锁是 同一个
        * 将释放锁的操作放在try...finally{}中,即便是当前线程出现异常也不影响其他线程的执行*/
        /*事实上从Hello World开始我们就已经在用多线程了,除了默认的main
        * 还一直存在一个垃圾回收线程 gc*/


        /*使用线程池来构建线程,阿里开发手册中严格禁止使用Executors来创建线程池*/
//        ExecutorService tpe = Executors.newFixedThreadPool(3);
        /*所以最好手动创建线程池,自定义拒绝策略,有效避免OOM
        * 使用ThreadPoolExecutor,该类中重载了许多构造器,但最终都会使用此构造器来完成构建
        * public ThreadPoolExecutor(int corePoolSize,线程池核心数量,其中的线程任务一直保留
                          int maximumPoolSize,最大线程数,当所有的核心线程均被占用时,线程池会开启新的线程
                          long keepAliveTime,线程存活时间,当线程任务结束后在规定时间内没有再次开启任务则自动关闭
                          TimeUnit unit,用于设置存活时间的单位
                          BlockingQueue<Runnable> workQueue,当所有线程均被占用时,新进来的任务会进入等待队列,当有线程执行完毕后
                          会从队列中取出任务继续执行
                          ThreadFactory threadFactory,作用是产生线程,可以自定义线程的类型
                          RejectedExecutionHandler handler 拒绝策略,设置当线程和队列满的时候对于新进来任务的处理方式
                          )
           线程队列:
            ArrayBlockingQueue :由数组结构组成的有界阻塞队列。
            LinkedBlockingQueue :由链表结构组成的有界阻塞队列。
            PriorityBlockingQueue :支持优先级排序的无界阻塞队列。
            DelayQueue: 使用优先级队列实现的无界阻塞队列。
            SynchronousQueue: 不存储元素的阻塞队列。
            LinkedTransferQueue: 由链表结构组成的无界阻塞队列。
            LinkedBlockingDeque: 由链表结构组成的双向阻塞队列


            拒绝策略:
             AbortPolicy 丢弃任务,抛出异常
             DiscardPolicy  使用execute方法执行任务
             DiscardOldestPolicy  抛弃线程池最前方的任务,并重新尝试执行新任务
             CallerRunsPolicy  直接丢弃任务
             当JDK提供的拒绝策略不满足需求时可以自定义拒绝策略,实现RejectedExecutionHandler接口并重写rejectedExecution方法
             将逻辑写在此方法中即可
        *
        *
        *
        * */
        ThreadPoolExecutor tp = new ThreadPoolExecutor(10,20,3,TimeUnit.SECONDS,
                new LinkedBlockingQueue(), new ThreadPoolExecutor.CallerRunsPolicy());
        /*使用submit方法向线程池提交一个任务*/
        tp.submit(() -> System.out.println("New ThreadPool"));

        /*如何测试多线程场景下的程序执行时间*/
        long begin = System.currentTimeMillis();
        a.count = 0;
        tp.submit(() -> {
            try {
                Thread.sleep(999);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a.count++;
        });
        /*这样不行,因为线程任务交给了线程池执行,main线程只进行了提交操作
        * 所以此处计算的时间并没有将线程任务执行时间包括在内*/
        System.out.println(System.currentTimeMillis() - begin);

        /*应该等待其他线程全部执行完毕后一起统计时间
        * 具体的实现:可以预先定义一个flag变量(如上面的a.count),当一个线程执行完毕之后该变量自增一
        * 当其值等于当前程序的线程数量时便打印执行时间*/
        
        /*具体实现*/
        while (a.count!=1){
            
        }
        System.out.println(System.currentTimeMillis() - begin);
        /*或者可以使用上文中FutureTask+Callable的方式
        get()方法获取到返回值前,调用线程会一直处于阻塞状态
        同样可以实现测试运行时间的操作*/

    }

牛哔
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值