多线程(总结黑马程序员)

一、什么是线程?

  • 是一个程序内部的一条执行流程

多线程是什么?

  • 多条线程由CPU负责调度执行

多线程的创建方式一:继承Thread类

//1.继承Thread类
public class MyThread extends Thread {
    //2.必须重写run方法
    @Override
    public void run() {
        for (int i = 1; i <= 5 ; i++) {
            System.out.println("子线程MyThread输出 :" + i);
        }
    }
}



public class ThreadTest1 {
    //main方法是由一条默认的主线程负责执行
    public static void main(String[] args) {
        //3.创建MyThread线程类的对象代表一个线程
        Thread t = new MyThread();
        //4.启动线程(自动执行run方法)
        t.start();

        for (int i = 1; i <= 5 ; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}

多线程的创建方式二:实现Runnable接口

//1.实现Runnable接口
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5 ; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        Runnable target = new MyRunnable();
        new Thread(target).start();

        for (int i = 1; i <= 5 ; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}

匿名内部类的写法

public class ThreadTest1 {
    public static void main(String[] args) {
        //直接创建Runnable接口的匿名内部类
        Runnable target = new MyRunnable(){
            @Override
            public void run() {
                for (int i = 1; i <= 5 ; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };
        new Thread(target).start();

        //简化形式1:
        new Thread(new MyRunnable(){
            @Override
            public void run() {
                for (int i = 1; i <= 5 ; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();

        //简化形式2
        new Thread(() -> {
                for (int i = 1; i <= 5 ; i++) {
                    System.out.println("子线程3

多线程的创建方式三:;利用Callable接口、FutureTask类来实现

//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 = 1; i <= n ; i++) {
            sum += i;
        }
        return "线程求出了1-" + n + "的和是:" + sum;


    }
}

  public static void main(String[] args) throws ExecutionException, InterruptedException {
        //3.创建一个Callable对象
        Callable call = new MyCallable(100);
        //4.把Callable的对象封装成一个FutureTask对象
        //未来对象的作用?
        //1.是一个任务对象,实现了Runnable对象
        //2.可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后
        FutureTask<String> f1 = new FutureTask<>(call);
        new Thread(f1).start();

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


        //6.获取线程执行完毕后返回的结果
        //注意:如果执行到这,假如上面的线程还没有执行完毕
        //这里的代码会暂停,等待上面线程执行完毕后才会获取结果
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2 );

    }

Thread常用方法

public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread("1号线程");
        t1.start();
        System.out.println(t1.getName());

        MyThread t2 = new MyThread("2号线程");
        t2.start();
        System.out.println(t2.getName());

        //主线程对象的名字
        //哪个线程执行它,它就会得到哪个线程对象
        Thread m = Thread.currentThread();
        m.setName("最diao的线程");
        System.out.println(m.getName());

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


public class MyThread extends Thread {
    public MyThread(String name) {
        super(name); //为当前线程设置名字
    }

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        for (int i = 1; i <= 3 ; i++) {
            System.out.println();
        }
    }
}

二、线程安全问题 

出现原因:

  • 存在多个线程同时执行
  • 同时访问一个共享资源
  • 存在修改该共享资源

取钱案例

需求:小明和小红有一个共同的账户,余额是10万元,模拟2人同时去取钱10万

  • 测试类
public class ThreadTest {
    public static void main(String[] args) {
        //1.创建一个账户对象,代表两个人的共享账户
        Account acc = new Account("ICBC-110", 100000);
        //2.创建两个线程,分别代表小明 小红,再去同一个账户对象中取钱10万
        new DrawThread(acc,"小明").start();
        new DrawThread(acc,"小红").start();
    }
}
  • 线程类
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 Account {

    private String cardId; //卡后
    private double money;  //账户余额

    public Account() {
    }

    public void drawMoney(double money) {
        //先搞清楚是谁来取钱
        String name = Thread.currentThread().getName();
        //1.判断余额是否够
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "取钱后,剩余余额:" + this.money);
        }else{
            System.out.println(name + "来取钱,余额不足");
        }
    }

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

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

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

}

三、线程同步

  • 解决线程安全问题的方案

线程同步的思想

  • 让多个线程实现先后依次访问共享资源

常见方案

  • 加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来

方式1:同步代码块

  • 作用:把访问共享资源的核心代码给上锁,保证线程安全
  • 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
public void drawMoney(double money) {
        //先搞清楚是谁来取钱
        String name = Thread.currentThread().getName();
        //1.判断余额是否够
        //this代表共享资源
        synchronized (this) {
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "取钱后,剩余余额:" + this.money);
            }else{
                System.out.println(name + "来取钱,余额不足");
            }
        }
    }

方式2:同步方法

  • 作用:把访问共享资源的核心方法给上锁
  • 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
 public synchronized void drawMoney(double money) {

}

同步方法底层原理 

  • 如果方法是实例方法:默认用this作为锁的对象
  • 如果方法是静态方法:默认用类名.class作为锁的对象

是同步代码块好还是同步方法好?

  • 范围上:同步代码块锁的范围更小,同步方法锁的范围更大
  • 可读性:同步方法更好

方式3:Lock锁

  • Lock锁是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象
 //创建了一个锁对象
    private final Lock lk = new ReentrantLock();


public void drawMoney(double money) {
        //先搞清楚是谁来取钱
        String name = Thread.currentThread().getName();
        try {
            lk.lock(); //加锁
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "取钱后,剩余余额:" + this.money);
            }else{
                System.out.println(name + "来取钱,余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lk.unlock();//解锁
        }
    }

四、线程池

什么是线程池?

  • 一个可以复用线程的技术

不使用线程池的原因

  • 创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,严重影响系统的性能

谁代表线程池?

  • 代表线程池的接口:ExEcuatorService

如何得到线程池对象?

 

  • 方式1:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
public class ThreadPoolTest {
    public static void main(String[] args) {
//        public ThreadPoolExecutor(int corePoolSize,
//                                    int maximumPoolSize,
//                                    long keepAliveTime,
//                                    TimeUnit unit,
//                                    BlockingQueue<Runnable> workQueue,
//                                    ThreadFactory threadFactory,
//                                    RejectedExecutionHandler handler) {
        //1.创建一个线程池对象
      ExecutorService pool =   new ThreadPoolExecutor(3,5,8,
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

   //2.使用线程池处理Callable任务
        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));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());

 Executors工具类实现线程池

 //1-2 通过Executors创建线程池对象
      ExecutorService pool = Executors.newFixedThreadPool(3);
      //核心线程数量到底配置多少呢?
      //计算密集型的任务:核心线程数量 = CPU的核数 + 1
      //IO密集型的任务:核心线程数量 = CPU的核数 + 2

五、线程的并发、并行和生命周期

进程

  • 正在运行的程序就是一个独立的进程
  • 进程中的多个线程其实是并发并行执行的

并发的含义

  • 进程中的线程是由CPU调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能执行,CPU会轮询为系统的每个线程服务

并行的理解

  • 在同一个时刻,同时有多个线程在被CPU调度

线程的生命周期

  • 也就是线程从生到死的过程,经历的各种状态及状态转换

JAVA线程的状态

  • 总共定义6种状态
  • 都定义在Thread类的内部枚举类中
public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called {@code Object.wait()}
         * on an object is waiting for another thread to call
         * {@code Object.notify()} or {@code Object.notifyAll()} on
         * that object. A thread that has called {@code Thread.join()}
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值