多线程总结

一.创建多线程方式

1.采用extends Thread方法实现
优点:编码简单
缺点:线程类已经继承 Thread ,无法继承其他类,不利于扩展。
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //获取线程名字
            String name = getName();
            System.out.println(name + "下载进度" + i + "%");
        }
    }
}


/**
 * 目标:采用extends Thread方法实现线程
 * 步骤如下:
 * 第一步: 写一个MyThread类继承Thread类,复写run方法
 * 第二步: 创建MyThread类的对象
 * 第三步: 调用start方法,启动线程
 */
public class Demo {
    public static void main(String[] args) {
        //创建MyThread对象
        MyThread t1 = new MyThread();
        //调用start方法启动线程
        t1.start();
        MyThread t2 = new MyThread();
        t2.start();
    }
}
2.采用implements Runnable 接口方式创建线程
优点:线程任务类只是实现了 Runnale 接口,可以继续继承和实现。

步骤如下:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <100; i++) {
            //可以使用Thread的静态方法currentThread()获取名字
            Thread t = Thread.currentThread();
            String name = t.getName();
            System.out.println(name+"下载进度" + i + "%");
        }
    }
}



/**
 * 目标:采用implements Runnable接口方式创建线程
 *
 * 解释:Runnable接口的实现类,我们可以把它看做线程任务。该实现方式,就是将线程与线程任务剥离出来。
 *
 * 步骤如下:
 *      第一步: 写一个MyRunnable类实现Runnable接口,复写run方法
 *      第二步: 创建MyRunnable类的对象
 *      第三步: 创建Thread对象,将MyRunnable对象作为参数传递进来
 *      第四步: 调用start方法,启动线程。
 */
public class Demo {
    public static void main(String[] args) {
        //创建MyRunnable类的对象
        MyRunnable mr = new MyRunnable();
        //创建Thread对象,将MyRunnable对象作为参数传递进来
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        t1.start();
        t2.start();
    }
}

 3.采用implements Callable接口方式创建线程

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编码复杂一点。

步骤如下:

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}

**
 * 目标:采用implements Callable接口方式创建线程。
 * <p>
 * 步骤如下:
 * 第一步: 写一个MyCallable类实现Callable接口,复写call方法
 * 第二步: 创建MyCallable类的对象
 * 第三步: 创建FutureTask对象,将MyCallable对象作为参数封装进来
 * 第四步: 创建Thread对象,将FutureTask对象作为参数封装进来
 * 第五步: 调用start方法,启动线程。
 */
public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyCallable类的对象
        MyCallable mc = new MyCallable();
        // 创建FutureTask对象,将MyCallable对象作为参数封装进来
        FutureTask<Integer> task = new FutureTask<>(mc);
        //创建Thread对象,将FutureTask对象作为参数封装进来
        Thread t1 = new Thread(task);
        t1.start();
        //用FutureTask中get来获取方法中的返回值
        Integer rs = task.get();
        System.out.println(rs);
    }
}

二.Thread常用方法

Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread() 、sleep(long  time)休眠时间单位毫秒

三.线程安全

多个线程操作同一个共享资源时可能会出现业务安全问题,称为线路安全问题

代码演示如下


/*
需求:小明和小红是一对夫妻有一个共同账户有余额10万元钱,使用多线程模拟小明和小红同时来取钱的过程。
 */
public class Demo {
    public static void main(String[] args) {
        Account account = new Account();
        DrawThread t1 = new DrawThread(account);
        t1.setName("小明");
        DrawThread t2 = new DrawThread(account);
        t2.setName("小红");
        t1.start();
        t2.start();
    }
}


public class Account {
    private int money = 100000;

    public void drawMoney(int money) {
        String name = Thread.currentThread().getName();
        if (this.money >= money) {
            try {

                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.money -= money;
            System.out.println(name + "取走了" + money + "还剩" + this.money);
        } else {
            System.out.println("余额不足!");
        }

    }
}

public class DrawThread extends Thread{
    private  Account account;
    public DrawThread( Account account){
        this.account=account;
    }

    @Override
    public void run() {
        account.drawMoney(100000);
    }
}

四.线程同步

1.解决线程安全问题

(1)synchronized(同步锁对象){操作共享资源的核心代码}

加锁每次只能让一个线程访问共享数据,其他线程在外等待

 //取钱
    public void drawMoney(int money) {
        //先获取当前的线程名称
        String name = Thread.currentThread().getName();
        //练习: 给下面的代码加上同步代码块  synchronized(同步锁对象){操作共享资源的核心代码}
        //同步锁对象要求
        /**
         * 规范上:建议使用共享资源作为锁对象。
         * 对于实例方法建议使用this作为锁对象。
         * 对于静态方法建议使用字节码(类名.class)对象作为锁对象
         */
        synchronized (this) {
            if (this.money >= money) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.money -= money;
                System.out.println(name + "来取钱了,取了" + money + "元,还剩" + this.money + "元");
            } else {
                //如果钱不够,打印输出余额不足.
                System.out.println(name + "余额不足");
            }
        }
    }

(2)同步方法

* 方法二 同步方法
* 1.同步方法是有锁对象的 只是没有显示出来
* 2.如果是实例方法锁对象是this
* 3.如果是静态方法锁对象是类的字节码 类名.class
* 修饰符 synchronized 返回值类型 方法名称(形参列表) {操作共享资源的代码}
public synchronized void drawMoney(int money) {
        //先获取当前的线程名称
        String name = Thread.currentThread().getName();
        //练习: 给下面的代码加上同步代码块
        if (this.money >= money) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.money -= money;
            System.out.println(name + "来取钱了,取了" + money + "元,还剩" + this.money + "元");
        } else {
            //如果钱不够,打印输出余额不足.
            System.out.println(name + "余额不足");
        }

(3)lock锁

Lock lock = new ReentrantLock()定义锁对象
lock.lock();//上锁
lock.unlock();//释放锁

五,线程池

ThreadPoolExecutor实现线程池对象,通过Runnable接口实现

1.线程池就是一个可以复用线程的技术

2. 核心线程都在忙 + 任务队列满了,就会创建临时线程

3.核心线程都在忙 + 临时线程也忙 + 任务队列也满了,多余的任务就会触发拒绝策略

*
 * 目标:使用ThreadPoolExecutor类去创建线程池,并使用submit方法提交Runnable任务
 * 从 API看构造方法的参数:
        corePoolSize - 池中所保存的线程数,包括空闲线程。
        maximumPoolSize - 池中允许的最大线程数。
        keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
        unit - keepAliveTime 参数的时间单位。
        workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
        threadFactory - 执行程序创建新线程时使用的工厂。
        handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
 */
public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数  corePoolSize
                5,//允许最大线程数  maximumPoolSize
                2,//临时线程存活时间  keepAliveTime
                TimeUnit.SECONDS,//时间单位  unit - keepAliveTime
                new ArrayBlockingQueue<>(10),//任务队列  workQueue
                Executors.defaultThreadFactory(),//获取默认的线程工厂  threadFactory
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略  handler
        );
        //拒绝策略触发时机=任务量>最大线程数+任务队列长度
        //执行Runnable1
        for (int i = 0; i < 15; i++) {
            int j=i;
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    //获取线程名称
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"个任务在执行"+j+"号任务");
                }
            });
        }
        //关闭线程池
        pool.shutdown();

(2)

使用ThreadPoolExecutor创建线程池,并提交Callable任务。

* 目标:使用ThreadPoolExecutor创建线程池,并提交Callable任务。
 */
public class Demo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                4,//核心线程数  corePoolSize
                5,//允许最大线程数  maximumPoolSize
                2,//临时线程存活时间  keepAliveTime
                TimeUnit.SECONDS,//时间单位  unit - keepAliveTime
                new ArrayBlockingQueue<>(10),//任务队列  workQueue
                Executors.defaultThreadFactory(),//获取默认的线程工厂  threadFactory
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略  handler
        );
        for (int i = 0; i < 16; i++) {
            Future<Integer> f = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i <= 100; i++) {
                        sum += i;
                    }
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "执行了!");
                    return sum;
                }
            });
            Integer rs = f.get();
            System.out.println(rs);
        }
        pool.shutdown();
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值