Java SE多线程

目录

1️⃣多线程的创建

🗝️继承Thread类

🗝️实现Runnable接口

🗝️JDK 5.0新增:实现Callable接口

2️⃣Thread的常用方法

 3️⃣线程安全(小案例)

 4️⃣线程同步

🗝️方式一:同步代码块

 🗝️方式二:同步方法

🗝️方式三:Lock锁

 5️⃣线程通信

6️⃣线程池

🗝️线程池概述

🗝️线程池实现的API 参数说明

🗝️线程池处理Runnable任务

🗝️线程池处理Callable任务

🗝️Executors工具类实现线程池

 7️⃣定时器


1️⃣多线程的创建

🗝️继承Thread类

 

/**
  目标:多线程的创建方式之一 继承Thread类
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //3new一个线程对象
        Thread t = new MyThread();
        //4启动线程对象
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行了 " + i);
        }
    }
}

/**
  1定义一个线程类继承Thread
 */
class MyThread extends Thread{
    /**
      2重写run方法
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程执行了 " + i);
        }
    }
}

 优点:编码简单

 缺点:线程类已经继承Thread,无法继承其他类,不利于扩展

注意🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔🔔

        不可以直接调用RUN方法 否则会直接当成单线程执行

        主线程任务放在子线程任务之后

🗝️实现Runnable接口

 

public class ThreadDemo2 {
    public static void main(String[] args) {
        //3创建一个任务对象
        Runnable target = new MyRunnable();
        //4把任务对象交给Thread处理
        Thread t = new Thread(target);
        //5启动线程
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行了 " + i);
        }
    }
}

/**
  1定义一个线程任务类实现Runnable接口
 */
class MyRunnable implements Runnable{
    /*
      重写run方法,定义线程的执行任务
     */

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程执行了 " + i);
        }
    }
}

 优点:线程任务类只是实现接口,可以继续继承类和实现其他接口,拓展性强

 缺点:编程多一层对象包装,如果线程执行结果是不可以直接返回的

🗝️JDK 5.0新增:实现Callable接口

 

/**
  目标:学会线程的创建方式三,实现Callable接口,结合FutureTask完成
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //3创建任务对象
        Callable<String> call = new MyCallable(100);
        //4把Callable任务对象  交给FutureTask 对象
        //FutureTask的作用1:是Runnable的对象(实现了Runnable接口),可以交给Thread了
        //FutureTask的作用2:可以在线程执行完毕后调用get方法得到线程完成的结果
        FutureTask<String> f1 = new FutureTask<>(call);
        //5交给线程处理
        Thread t1 = new Thread(f1);
        t1.start();

        Callable<String> call1 = new MyCallable(200);
        FutureTask<String> f2 = new FutureTask<>(call1);
        Thread t2 = new Thread(f2);
        t2.start();

        try {
            String rs1 =  f1.get();
            System.out.println("第一个结果 " + rs1);

        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            String rs2 =  f2.get();
            System.out.println("第er个结果 " + rs2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 *  1定义一个任务类 实现Callable接口
 */
class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    /*
          2重写call方法(任务方法)
         */
    @Override
    public String call() throws Exception {
       int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "子线程的结果是" + sum;
    }
}

优点:可以返回线程执行的结果

2️⃣Thread的常用方法

⚫Thread的常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象             currentThread()

 

public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        //为当前线程对象设置名称,送给父类的有参数构造器初始化名称
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "输出 " + i );
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread("1号");
        //t1.setName("1号");
        t1.start();
        System.out.println(t1.getName());

        Thread t2 = new MyThread("2号");
        //t2.setName("2号");
        t2.start();
        System.out.println(t2.getName());

        //哪个线程执行它,他就得到哪个线程对象(当前线程对象)
        Thread m= Thread.currentThread();
        System.out.println(m.getName());
        m.setName("哇哈哈");

        for (int i = 0; i < 5; i++) {
            System.out.println(m.getName() + "输出 " + i );
        }
    }
}

 3️⃣线程安全(小案例)

为什么会出现线程安全?

       ①存在多线程并发

       ②同时访问共享资源

       ③存在修改共享资源

小案例:小敏和小红是夫妻,他们有一个共同账户,余额10万元,模拟两人同时取钱

账户信息:⬇️⬇️⬇️⬇️⬇️⬇️

public class Account {
    private String ID;
    private double money;

    public Account(String ID, double money) {
        this.ID = ID;
        this.money = money;
    }

    public String getID() {
        return ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
        //1先获取谁来取钱
        String name = Thread.currentThread().getName();
        //2判断余额是否充足
        if (this.money >= money){
            //3取钱
            System.out.println(name + "来取钱,吐出" + money);
            //4更新
            this.money -= money;
            System.out.println(name + "取钱后剩余" + this.money);

        }else {
            //5余额不足
            System.out.println(name + "来取钱,余额不足");
        }

    }
}

 取钱线程类⬇️⬇️⬇️⬇️⬇️⬇️

/**
 * 取钱线程类
 */
public class DrawThread extends Thread{
    //接受处理账户对象
    private Account acc;
    public DrawThread(Account acc,String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        //取钱的
        acc.drawMoney(100000);
    }
}

 

/**
 * 模拟取钱案例
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //1定义一个线程类,创建共享账户对象
        Account acc = new Account("ICBU-123",100000);

        //2创建两个线程对象
        new DrawThread(acc,"小敏").start();
        new DrawThread(acc,"小红").start();
    }
}

结果⬇️⬇️⬇️⬇️⬇️⬇️⬇️(出现安全问题)

 4️⃣线程同步

线程同步核心思想:把共享资源加上锁,每次只能一个线程进入访问完毕后再解锁其他线程再进来

🗝️方式一:同步代码块

 

    //同步代码块
//    public void drawMoney(double money) {
             //this == acc
//        synchronized (this) {    //代表当前对象
//            //1先获取谁来取钱
//            String name = Thread.currentThread().getName();
//            //2判断余额是否充足
//            if (this.money >= money){
//                //3取钱
//                System.out.println(name + "来取钱,吐出" + money);
//                //4更新
//                this.money -= money;
//                System.out.println(name + "取钱后剩余" + this.money);
//
//            }else {
//                //5余额不足
//                System.out.println(name + "来取钱,余额不足");
//            }
//        }
//
//    }

 🗝️方式二:同步方法

 

    //同步方法
//    public synchronized void drawMoney(double money) {
//
//            //1先获取谁来取钱
//            String name = Thread.currentThread().getName();
//            //2判断余额是否充足
//            if (this.money >= money){
//                //3取钱
//                System.out.println(name + "来取钱,吐出" + money);
//                //4更新
//                this.money -= money;
//                System.out.println(name + "取钱后剩余" + this.money);
//
//            }else {
//                //5余额不足
//                System.out.println(name + "来取钱,余额不足");
//            }
//
//
//    }

🗝️方式三:Lock锁

 

 //LOCK锁
    public  void drawMoney(double money) {

        //1先获取谁来取钱
        String name = Thread.currentThread().getName();
        //2判断余额是否充足
        //小敏 小红
        lock.lock();  //上锁
        try {
            if (this.money >= money){
                //3取钱
                System.out.println(name + "来取钱,吐出" + money);
                //4更新
                this.money -= money;
                System.out.println(name + "取钱后剩余" + this.money);

            }else {
                //5余额不足
                System.out.println(name + "来取钱,余额不足");
            }
        } finally {
            lock.unlock();//解锁
        }


    }

 5️⃣线程通信

什么是线程通信、如何实现❓

⚫所谓线程通信就是线程间相互发送数据,线程间共享一个资源即可实现

线程通信实际应用场景

⚫生产者与消费者模型:生产者线程负责生产数据,消费者线程负责消费生产者产生的数据

⚫要求:生产者生产完数据后唤醒消费者,然后等待自己,消费者消费完以后唤醒生产者,然后等待自己

 同上Account账户内容添加

public synchronized void drawMoney(double money) {
        try {
            String name = Thread.currentThread().getName();
            if (this.money >= money){
                //钱够 可以取
                this.money -= money;
                System.out.println(name + "来取钱" + money + "成功!余额是" + this.money);
                //没钱了
                this.notifyAll();  //唤醒所有人
                this.wait(); //锁对象 让当前线程进入等待

            }else {
                //钱不够,不可以取
                //唤醒别人 等待自己
                this.notifyAll();  //唤醒所有人
                this.wait(); //锁对象 让当前线程进入等待


            }
        } catch (Exception e) {
            e.printStackTrace();
        }

6️⃣线程池

🗝️线程池概述

 什么是线程池?

⚫就是一个可以复用线程的技术

 

 

🗝️线程池实现的API 参数说明

 

🗝️线程池处理Runnable任务

 

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "输出了 " + i);

        }
        try {
            System.out.println(Thread.currentThread().getName() + "本任务与线程绑定了,进入休眠");
            Thread.sleep(10000000);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //1创建线程池对象
        /**
         public ThreadPoolExecutor(int corePoolSize,
                                   int maximumPoolSize,
                                   long keepAliveTime,
                                   TimeUnit unit,
                                   BlockingQueue<Runnable> workQueue,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler)
         */
        ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        //2给任务线程池处理
        Runnable target = new MyRunnable();
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        //开始创建临时线程
        pool.execute(target);
        pool.execute(target);
        //不创建了  拒绝策略触发  因为最多5个线程
        pool.execute(target);


    }
}

 

 

🗝️线程池处理Callable任务

 

/**
 *  1定义一个任务类 实现Callable接口
 */
public class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    /*
          2重写call方法(任务方法)
         */
    @Override
    public String call() throws Exception {
       int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "执行1-"+ n+ "结果是" + sum;
    }
}

 

public class ThreadPoolDemo2 {
    public static void main(String[] args) throws Exception {
        //1创建线程池对象
        /**
         public ThreadPoolExecutor(int corePoolSize,
                                   int maximumPoolSize,
                                   long keepAliveTime,
                                   TimeUnit unit,
                                   BlockingQueue<Runnable> workQueue,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler)
         */
        ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        //2给任务线程池处理
       Future<String> f1 = pool.submit(new MyCallable(100));
       Future<String> f2 = pool.submit(new MyCallable(200));
       Future<String> f3 = pool.submit(new MyCallable(300));
       Future<String> f4 = pool.submit(new MyCallable(400));
       Future<String> f5 = pool.submit(new MyCallable(500));

//       String rs = f1.get();
//       System.out.println(rs);
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
        System.out.println(f5.get());


    }
}

🗝️Executors工具类实现线程池

/**
  目标:使用Executors的工具方法直接得到一个线程池对象
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //1创建固定线数据的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);

        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());//已经没有多余的线程了


    }
}

 7️⃣定时器

 

 

 

 

/**
 * 目标  Timer定时器的使用和了解
 */
public class TimerDemo1 {
    public static void main(String[] args) {
        //1创建Timer定时器
        Timer timer = new Timer();  //定时器本身就是一个单线程
        //2调用方法,处理定时器任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行一次~~~~");
            }
        },3000,2000);
    }
}

 缺点:Timer是单线程,若因为某个异常使其死掉,会影响后续的执行

public class TimerDemo2 {
    public static void main(String[] args) {
        //1创建ScheduleExceutorService线程池,做定时器
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

       //2开启定时任务
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行输出:AA");
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },0,2, TimeUnit.SECONDS);

        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行输出:BB");
                System.out.println(10 / 0);
            }
        },0,2, TimeUnit.SECONDS);

        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行输出:CC");
            }
        },0,2, TimeUnit.SECONDS);
    }
}

 优点:基于线程池,某个任务执行挂掉不会影响后续执行

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值