Bootstrap

Java多线程

1.进程的概念


进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程。
简单来说电脑上运行的QQ、微信、谷歌浏览器分别就是一个一个的进程,它们之所以看着像同时运行,其实不然,在CPU中,每个软件是轮流运行的,而且每个运行的时间间隔很短,作为我们使用者来说,是感觉不到它的变化的。所以我们就会认为软件都是同时运行的,叫做时间片的轮转。

2.什么是线程

线程是比进程还要小的运行单位,一个进程包含多个线程(比如说一个程序是有很多行代码组成的,那么这些代码就可以分为很多块,放到不同的线程去分别执行),线程可以看做一个子程序

3.线程的创建

  • 创建一个Thread类,或者Thread子类的对象
  • 创建一个实现Runnable接口的类的对象

Thread类的构造方法
在这里插入图片描述
Thread类的常用方法
在这里插入图片描述

Runnable接口
在这里插入图片描述

4.通过Thread类创建线程

package com.imooc.thread;

public class MyThread  extends Thread{

    public MyThread(String name){
        super(name);//调用父类的有参构造犯法
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(getName()+"线程正在执行"+i);
        }

    }
}

package com.imooc.thread;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("线程1");
        MyThread mt2 = new MyThread("线程2");
        mt1.start();//启动线程1
        mt2.start();//启动线程2

    }
}

5.通过Runnable接口创建线程

package com.imooc.runnable;

public class PrintRunnable implements Runnable {
    public void run() {
        System.out.println(Thread.currentThread().getName()+"正在运行");
    }
}

package com.imooc.runnable;

public class Test {
    public static void main(String[] args) {
        //通过实现Runnable接口来创建线程
        PrintRunnable printRunnable = new PrintRunnable();
        Thread thread = new Thread(printRunnable);
        thread.start();

    }
}

6.线程的状态和生命周期

线程的状态

新建(New)
可运行(Runnable)
正在运行(Running)
阻塞(Blocked)
中止(Dead)

线程的生命周期
在这里插入图片描述

7.sleep方法的使用


Thread类的静态方法
作用:在指定的毫秒数让正在执行的线程休眠(暂停执行)
参数为休眠的时间,单位是毫秒

package com.imooc.runnable;

public class PrintRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName()+"正在运行第"+i+"次");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

8.join方法的使用


Thread类的方法
public final void join() 最终的方法,不能被重写
作用:等待调用join方法的线程结束后才能执行(是一种抢占资源的方式)

package com.imooc.runnable;

public class Test {
    public static void main(String[] args) {
        //通过实现Runnable接口来创建线程
        PrintRunnable printRunnable = new PrintRunnable();
        Thread thread = new Thread(printRunnable);
        thread.start();
        try {
            //调用了join方法的线程优先执行
            //如果加了join方法加了参数毫秒,那么意味着该线程最多执行多少毫秒后,就会把程序执行主动权放出来
            thread.join();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("主线程运行结束");
    }
}

9.线程的优先级

  • Java为线程类提供了10个优先级
  • 优先级可以用整数1-10表示,超过范围会抛出异常
  • main方法主线程默认优先级为5
  • 数字越大表示优先级越高

还可以用优先级常量表示
MAX_PRIORITY:线程的最高优先级10
MIN_PRIORITY:线程的最低优先级1
NORM_PRIORITY:线程的默认优先级5

优先级相关的方法
在这里插入图片描述

特别说明:优先级大的不一定先执行,这跟系统的操作系统的环境,CPU的工作方式都是有很大的关系

package com.imooc.runnable;

public class PrintRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");
        }

    }
}

package com.imooc.runnable;

public class Test2 {

    public static void main(String[] args) {
        PrintRunnable pr = new PrintRunnable();
        PrintRunnable pr2 = new PrintRunnable();

        Thread t1 = new Thread(pr,"我是线程1");//线程2
        Thread t2 = new Thread(pr2,"我是线程2");//线程2

        t1.setPriority(Thread.MIN_PRIORITY);//设置最小优先级
        t2.setPriority(Thread.MAX_PRIORITY);//设置最大优先级

        //说明:优先级大的不一定先执行,这跟系统的操作系统的环境,CPU的工作方式都是有很大的关系

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

    }
}

执行效果:
在这里插入图片描述

10.线程同步

多线程的运行问题

  • 各个线程是通过竞争CPU时间而获得运行机会的
  • 各线程什么时候得到CPU时间,占用多久,是不可预测的
  • 一个正在运行的线程什么地方被暂停是不确定的

银行存取款问题

  • 为了包装存款或取款的时候,不允许其他线程对账号余额进行操作
  • 需要将Bank对象进行锁定
  • 使用关键字synchronized实现
    在这里插入图片描述
    具体代码
package com.imooc.runnable;

public class Bank {
    private String account;//账号
    private int balance;//账户余额

    public Bank(String account, int balance) {
        this.account = account;
        this.balance = balance;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Bank{" +
                "account='" + account + '\'' +
                ", balance=" + balance +
                '}';
    }

    //存款方法 synchronized:同步操作,当前方法没执行完的时候不允许其他线程操作
    public  synchronized  void saveAccount(){
        //获取余额
        int balance = getBalance();

        //存100元进去
        balance+=100;

        //模拟正常的耗时操作
        try {
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }

        //保存余额
        setBalance(balance);

        System.out.println("存款后的账户余额为:"+balance);
    }


    //取款方法 synchronized:同步操作,当前方法没执行完的时候不允许其他线程操作
    public  void drawAccount(){
        synchronized (this){
            int balance = getBalance();


            //取200元
            balance -= 200;
            //模拟正常的耗时操作
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //保存余额
            setBalance(balance);

            System.out.println("取款后的账户余额为:" + balance);
        }





    }
}


package com.imooc.runnable;

public class SaveAccount implements Runnable{

    //声明一个bank对象
    Bank bank;

    public SaveAccount(Bank bank) {
        this.bank = bank;
    }

    @Override
    public void run() {
        //使用线程进去存款操作
        bank.saveAccount();
    }
}

package com.imooc.runnable;

public class DrawAccount implements Runnable{

    Bank bank;
    public DrawAccount(Bank bank) {
        this.bank = bank;
    }
    @Override
    public void run() {
        //使用线程进行取款操作
        bank.drawAccount();
    }
}

package com.imooc.runnable;

public class BankTest {
    public static void main(String[] args) {

        //声明银行对象,设置账号和初始金额
        Bank bank = new Bank("1001",1000);

        //创建线程

        //1.存款线程
        SaveAccount saveAccount = new SaveAccount(bank);
        //2.取款线程
        DrawAccount drawAccount = new DrawAccount(bank);

        Thread thread1 = new Thread(saveAccount);
        Thread thread2 = new Thread(drawAccount);

        //3.启动线程
        thread1.start();
        thread2.start();

        //4.优先执行两个线程
        try{
            thread1.join();
            thread2.join();
        }catch (Exception e){
            e.printStackTrace();
        }


        System.out.println(bank);
    }
}

上述问题就和并发问题类似,多线程抢占资源,就比如商品超卖问题(比如在商品数量还剩余1的情况下,A线程正在进行判断商品的数量是否合法,接下来执行扣完库存操作,然而B线程却在A线程还没来得及执行扣库存操作的情况下也进入了这个商品判断,导致B线程也执行了扣库存操作,这就导致了商品为负数)在PHP项目中我之前采用的Redis分布式锁+lua脚本执行原子操作解决的这个问题。

11.线程间通信

  • wait()方法:中断方法的执行,使线程等待
  • notify()方法:唤醒处于等待的某一线程,使其结束等待
  • notifyAll()方法:唤醒所有处于等待的线程,使它们结束等待

队列类:

package com.imooc.queue;

public class Queue {
     private int n;

     boolean flag = false;
    public synchronized int getN() {
        //如果没数据则等待
        if (!flag) {
            try {
                wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费" + n);
        flag = false;
        //唤醒所有处于等待的线程
        notifyAll();
        return n;

    }

    public synchronized void setN(int n) {
        //如果有数据,则等待
        if(flag) {
            try {
                wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        System.out.println("生产" + n);
        this.n = n;
        flag = true;
        //唤醒所有处于等待的线程
        notifyAll();



    }
}

生产类:

package com.imooc.queue;

/**
 * 生产类
 */
public class Pro implements Runnable {
    Queue queue;
    public Pro(Queue queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        int i=0;
        while (true) {
            queue.setN(i++);
            //休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

消费类:

package com.imooc.queue;

public class Cons implements Runnable {
    Queue queue;
    public Cons(Queue queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        while (true) {
            queue.getN();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

测试类:

package com.imooc.queue;

public class Test {
    public static void main(String[] args) {
        Queue queue = new Queue();

        //生产者
        Pro pro = new Pro(queue);
        //消费者
        Cons con = new Cons(queue);


        Thread thread1 = new Thread(pro);
        Thread thread2 = new Thread(con);

        thread1.start();
        thread2.start();
    }
}

实现效果:
在这里插入图片描述

;