Java多线程和自定义线程池

Java多线程

一、什么是多线程?

  线程是操作系统能够进行运算调度的最小单位。被包含在进程之中,是进程中的实际运作单位。

  进程是程序基本执行实体。

而Java多线程则是指在一个Java程序中同时运行多个线程,每个线程可以独立执行不同的任务。多线程允许程序在同一时间内处理多个操作,从而提高程序的效率和响应速度。在Java中,线程是程序执行的最小单位,多线程编程可以充分利用多核处理器的计算能力,实现并发执行。

二、并发和并行

  并发:在同一时刻,有多个指令在单个CPU上交替执行。

  并行:在同一时刻,有多个指令在多个CPU上同时执行。

三、Java中三种实现多线程的方法

  1.自己定义一个类集成Thread类,重写run方法,创建子类的对象,并启动线程;

public class MyThread extends Thread{

    @Override
    public void run() {
        //书写线程要执行代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "HelloWorld");
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        /*
        * 多线程的第一种启动方式:
        *   1. 自己定义一个类继承Thread
        *   2. 重写run方法
        *   3. 创建子类的对象,并启动线程
        * */


        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("线程1--->");
        t2.setName("线程2--->");
        //start()方法表示开启线程
        t1.start();
        t2.start();



    }
}

2.自己定义一个类实现runnable接口,重写run方法,创建自己的类对象,创建一个Thread类的对象,并开启线程

public class MyRun implements Runnable{

    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            //获取到当前线程的对象
            /*Thread t = Thread.currentThread();
            System.out.println(t.getName() + "HelloWorld!");*/
            System.out.println(Thread.currentThread().getName() + "HelloWorld!");
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        /*
         * 多线程的第二种启动方式:
         *   1.自己定义一个类实现Runnable接口
         *   2.重写里面的run方法
         *   3.创建自己的类的对象
         *   4.创建一个Thread类的对象,并开启线程
         * */


        //创建MyRun的对象
        //表示多线程要执行的任务
        MyRun mr = new MyRun();

        //创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        //给线程设置名字
        t1.setName("线程1");
        t2.setName("线程2");


        //开启线程
        t1.start();
        t2.start();





    }
}

3.创建一个类MyCallable实现Callable接口,重写call (是有返回值的,表示多线程运行的结果),创建MyCallable的对象(表示多线程要执行的任务),创建FutureTask的对象(作用管理多线程运行的结果),创建Thread类的对象,并启动(表示线程)。

public class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        //求1~100之间的和
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum = sum + i;
        }
        return sum;
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

            /*
            *   多线程的第三种实现方式:
            *       特点:可以获取到多线程运行的结果
            *
            *       1. 创建一个类MyCallable实现Callable接口
            *       2. 重写call (是有返回值的,表示多线程运行的结果)
            *
            *       3. 创建MyCallable的对象(表示多线程要执行的任务)
            *       4. 创建FutureTask的对象(作用管理多线程运行的结果)
            *       5. 创建Thread类的对象,并启动(表示线程)
            * */

        //创建MyCallable的对象(表示多线程要执行的任务)
        MyCallable mc = new MyCallable();
        //创建FutureTask的对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mc);
        //创建线程的对象
        Thread t1 = new Thread(ft);
        //启动线程
        t1.start();

        //获取多线程运行的结果
        Integer result = ft.get();
        System.out.println(result);


    }
}

四、多线程中常见成员方法和其他线程

常见成员方法如下:

 /*
            String getName()                    返回此线程的名称
            void setName(String name)           设置线程的名字(构造方法也可以设置名字)
            细节:
                1、如果我们没有给线程设置名字,线程也是有默认的名字的
                        格式:Thread-X(X序号,从0开始的)
                2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置

            static Thread currentThread()       获取当前线程的对象
            细节:
                当JVM虚拟机启动之后,会自动的启动多条线程
                其中有一条线程就叫做main线程
                他的作用就是去调用main方法,并执行里面的代码
                在以前,我们写的所有的代码,其实都是运行在main线程当中

            static void sleep(long time)        让线程休眠指定的时间,单位为毫秒
            细节:
                1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
                2、方法的参数:就表示睡眠的时间,单位毫秒
                    1 秒= 1000毫秒
                3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
       */

 /*
            setPriority(int newPriority)        设置线程的优先级
            final int getPriority()             获取线程的优先级
       */

关于其他线程

守护线程:将线程2设置守护线程来守护线程1。

public class MyThread1 extends Thread{

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(getName() + "@" + i);
        }
    }
}
public class MyThread2 extends Thread{

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "@" + i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
       /*
            final void setDaemon(boolean on)    设置为守护线程
            细节:
                当其他的非守护线程执行完毕之后,守护线程会陆续结束
            通俗易懂:
                当女神线程结束了,那么备胎也没有存在的必要了
       */



        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();

        t1.setName("女神");
        t2.setName("备胎");

        //把第二个线程设置为守护线程(备胎线程)
        t2.setDaemon(true);

        t1.start();
        t2.start();

    }
}

礼让线程(不常用):

public class MyThread extends Thread{

    @Override
    public void run() {//"飞机"  "坦克"
        for (int i = 1; i <= 100; i++) {

            System.out.println(getName() + "@" + i);
            //表示出让当前CPU的执行权
            Thread.yield();
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
       /*
            public static void yield()      出让线程/礼让线程

       */

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("飞机");
        t2.setName("坦克");

        t1.start();
        t2.start();


    }
}

插队线程:

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "@" + i);

        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
       /*
            public final void join()  插入线程/插队线程
       */



        MyThread t = new MyThread();
        t.setName("土豆");
        t.start();

        //表示把t这个线程,插入到当前线程之前。
        //t:土豆
        //当前线程: main线程
        t.join();

        //执行在main线程当中的
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程" + i);
        }


    }
}

五、线程安全和线程的生命周期

  1.线程安全:例如,当有这样一个需求时,

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

可能发生线程安全问题,导致电影票超卖问题

解决办法:利用同步代码块,即synchronized关键字/利用lock锁,让程序串行化执行,防止出现超卖问题。

解决思路:加锁(注意死锁问题)

利用synchronized关键字解决如下

public class MyRunnable implements Runnable {

    int ticket = 0;

    @Override
    public void run() {
        //1.循环
        while (true) {
            //2.同步代码块(同步方法)
            if (method()) break;
        }
    }

    //this
    private synchronized boolean method() {
        //3.判断共享数据是否到了末尾,如果到了末尾
        if (ticket == 100) {
            return true;
        } else {
            //4.判断共享数据是否到了末尾,如果没有到末尾
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket++;
            System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");
        }
        return false;
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
       /*
           需求:
                某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
                利用同步方法完成
                技巧:同步代码块
       */

        MyRunnable mr = new MyRunnable();

        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();










    }
}

利用lock加锁实现如下

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread{

    static int ticket = 0;

    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        //1.循环
        while(true){
            //2.同步代码块
            //synchronized (MyThread.class){
            lock.lock(); //2 //3
            try {
                //3.判断
                if(ticket == 100){
                    break;
                    //4.判断
                }else{
                    Thread.sleep(10);
                    ticket++;
                    System.out.println(getName() + "在卖第" + ticket + "张票!!!");
                }
                //  }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
       /*
           需求:
                某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
                用JDK5的lock实现
       */


        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}

线程的生命周期

1.新建

2.就绪

3.运行

4.阻塞

5.死亡

注意:当线程执行sleep方法后苏醒时,并不会立即执行下一行代码,而是进入就绪状态,抢夺CPU的执行权

在API帮助文档中关于多线程状态描述如下:

线程池

一、什么叫线程池

 线程池是一种多线程处理形式,用于在处理任务时优化线程的创建和管理。它预先创建一组线程,并将这些线程放入池中等待执行任务。当有任务需要执行时,线程池会分配一个空闲线程来执行该任务,任务完成后线程会返回池中等待下一个任务。

  不管是继承Thread类还是实现Rannable接口创建的线程,都有一个缺点,即用完即销毁,需要时创建,极大的增加了内存占用和资源的损耗,而线程池则很好的解决了这一问题。

二、Java中的线程池

  Java中的线程池主要分为两种:

  1.无上限的线程池->无限创建线程执行任务

  2.有限的线程池->有限个线程,当任务到达时,若无空闲线程,则任务等待

代码如下:

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}
public class MyThreadPoolDemo {
   /*
    public static ExecutorService newCachedThreadPool(); 创建一个无上限的线程池
    public static ExecutorService newFixedThreadPool();  创建一个有限的线程池
    */
    public static void main(String[] args) {
        //获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool();
        //提交任务
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
    }
}

创建有限个线程的线程池

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}
public class MyThreadPoolDemo {
   /*
    public static ExecutorService newCachedThreadPool(); 创建一个无上限的线程池
    public static ExecutorService newFixedThreadPool();  创建一个有限的线程池
    */
    public static void main(String[] args) {
        //获取线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //提交任务
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
    }
}

三、自定义线程池

  自定义线程首先要明确以下变量:

1.核心线程数(不能小于0)

2.线程池中最大线程的数量(核心线程数+临时线程数,最大数量>=核心线程数量)

3.空闲时间(临时线程空闲销毁时间:值)(不能小于0)

4.空闲时间(临时线程空闲销毁时间:单位)(用TimeUnit指定)

5.阻塞队列 (不为null)

6.创建线程的方式 (不为null)

7.要执行任务很多时的解决方案(不为null)

注意:当核心线程都在使用,且阻塞队列已经排满时,创建临时线程继续处理任务,而且先提交的任务不一定先完成。当所有核心线程和临时线程都在使用,且阻塞队列都排满的情况下,触发任务拒绝策略(默认丢弃任务)

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}
import java.util.concurrent.*;

public class MyThreadPoolDemo {

    public static void main(String[] args) {
        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                3, //核心线程数(不能小于0)
                6, //线程池中最大线程的数量(核心线程数+临时线程数,最大数量>=核心线程数量)
                60,//空闲时间(临时线程空闲销毁时间:值)(不能小于0)
                TimeUnit.SECONDS, //空闲时间(临时线程空闲销毁时间:单位)(用TimeUnit指定)
                new ArrayBlockingQueue<>(3), //阻塞队列 (不为null)
                Executors.defaultThreadFactory(), //创建线程的方式 (不为null)
                new ThreadPoolExecutor.AbortPolicy() //任务拒绝策略(不为null)
        );
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值