多线程

多线程

image-20220331213904991

进程、程序、线程

  • 程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程:是执行程序的一次执行过程,它是一个动态的概念。是系统分配的单位
  • 线程:通常在一个进程中额可以包含多个线程,当然一个进程中至少有一个线程,线程是CPU调度和执行的单位。
    • 注意:线程开启不一定马上执行,由CPU调度

注意:很多多线程都是模拟出来的,真正的多线程要调用多个CPU

image-20220331214559661

多线程的创建方式

  1. thread class
  • 继承 Thread 类(重点)
  • TestThread1 --- 简单展示多线程的特点
  1. Runnable 接口
  • 实现 Runnable 接口(重点)
  • TestThread2 --- 简单展示
  1. Callable 接口
  • 实现 Callable 接口(了解)

关于实现多线程的几种方法

继承Thread类

  • 子类继承Thread具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()

静态代理模式

  • 例子 :demo1.StaticProxy
  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色
  • 真实对象专注自己的事情
  • 其他事情交付给代理对象实现

Lambda 表达式

函数式接口

  • 任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口
  • 对于函数式接口可以通过lambda表达式来创建该接口的对象
  • 是使用lambda表达式的前提

小例子

package learnSpace.lambda;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class demo1 {
    public static void main(String[] args) {
        new Like().lambda();

        ILike like = ()->{
            System.out.println("lambda coming");
        };
        like.lambda();
    }
}

interface ILike{
    void lambda();
}

class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("test lambda");
    }
}

简化写法

  • 简化赋值括号和花括号
  • 但是如果逻辑有多行的时候就不能简化{}了
  • 同样如果有多个参数就不能省略()了,但是仍然可以省略参数类型
ILove t;
t = (int a)->{
    System.out.println("Test a = " + a);
};

t = a -> System.out.println("Simply a = " + a);

/*
t = (a,b)->{
	System.out.println("Simply a = " + a);
	System.out.println("Simply b = " + b);
}
*/

线程状态

image-20220401151315507

image-20220401151642760

线程停止

  1. 建议线程正常停止 ---> 利用次数,不建议死循环
  2. 建议使用标志位 ---> 设置一个标志位
  3. 不要使用 stop 、 destroy 等过时或 JDK 不建议使用的方法
  • 小例子
package learnSpace.statue;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestStop implements Runnable{
    //1. 设置一个标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("running...  i = " + i ++);
        }
    }

    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop t = new TestStop();
        new Thread(t).start();

        for(int i = 0;i < 1000;i ++){
            System.out.println("In main, i = " + i);
            if(i == 900){
                t.stop();
                System.out.println("Stop!");
            }
        }
    }
}

线程休眠

  • sleep(int miles);
    • 指定当前线程阻塞的毫秒数
  • sleep 时间达到后线程进入就绪状态
  • sleep 可以模拟网络延时、倒计时等
  • 每个对象都有一个锁,sleep 不会释放锁

线程礼让

  • 让当前执行的线程暂停,但不阻塞
  • 将线程从以女性状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功! > 看CPU如何调度,礼让不一定成功

线程强制执行

  • Join 合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

  • 可以理解成插队

  • 小例子

package learnSpace.statue;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i < 100;i ++){
            System.out.println("vip coming... "  + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin t = new TestJoin();
        Thread thread = new Thread(t);
        thread.start();

        for(int i =  0;i < 50;i ++){
            if(i == 30){
                thread.join();
                System.out.println("Join!!");
            }

            System.out.println("main... " + i);
        }
    }
}

线程状态观测实例

package learnSpace.statue;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("...");
        });

        //观察状态
        System.out.println(t.getState() + " before start");

        t.start();
        System.out.println(t.getState() + " after start");

        while(t.getState() != Thread.State.TERMINATED){
            Thread.sleep(100);
            System.out.println(t.getState());
        }
    }
}

线程的优先级

image-20220401173412987

  • 观测实例
package learnSpace.statue;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestPriority{
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());

        MyPriority t = new MyPriority();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        Thread t5 = new Thread(t);
        Thread t6 = new Thread(t);

        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();

        t5.setPriority(7);
        t5.start();

        t6.setPriority(8);
        t6.start();
    }
}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());
    }
}
  • 注意:并非优先级高的就一定先跑,还是根据CPU调度来决定先后顺序的,优先级高只是被调度的概率高

守护线程

image-20220401175318573

  • 一个不需要结束的线程
  • 实例
package learnSpace.statue;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);
        //守护线程不需要停止
        thread.start();

        new Thread(you).start();
    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("happy living...");
        }

        System.out.println("bye");
    }
}

class God implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("God's watching");
        }
    }
}

线程同步

  • 多个线程操作同一个资源

  • 形成条件:队列 + 锁

  • 并发问题:同一个对象被多个线程同时操作

  • 处理并发问题的时候就需要线程同步,多个需要访问的线程进入对象的等待池形成队列

队列和锁

  • 队列:解决并发问题形成的线程队列
  • 锁:上一个线程执行完毕就开锁,让下一个线程进来执行

使用锁会造成的问题

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起
  • 多线程竞争下,锁的操作会消耗额外的资源引起性能问题
  • 优先级高的线程等待优先级低的线程释放锁,会导致优先级倒置,引起性能问题

同步方法

image-20220401190013184

  • 方法中需要修改的内容才需要锁,锁的太多会浪费资源

同步块

image-20220401190859938

小总结

  • 同步方法锁的是this
  • 同步块锁的是会被修改的类

线程安全的JUC集合类型

package learnSpace.syn;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

死锁

  • 两个或多个线程都在等对方释放自己需要的资源,造成程序停止运行

  • 小例子

package learnSpace.thread;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/1
 */
public class DeadLock {
    public static void main(String[] args) {
        Makeup a = new Makeup(0, "A");
        Makeup b = new Makeup(1, "B");

        a.start();
        b.start();
    }
}

class Lipstick{}

class Mirror{}

class Makeup extends Thread{

    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;
    String girlName;

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        //死锁警告
        /*
        if(choice == 0){
            synchronized (lipstick){
                System.out.println("lipstick's lock " + Thread.currentThread().getName());
                Thread.sleep(1000);

                synchronized (mirror){
                    System.out.println("mirror's lock " + Thread.currentThread().getName());
                }
            }
        }else{
            synchronized (mirror){
                System.out.println("mirror's lock " + Thread.currentThread().getName());
                Thread.sleep(2000);

                synchronized (lipstick){
                    System.out.println("lipstick's lock " + Thread.currentThread().getName());
                }
            }
        }
        */


        //不抱住对方的锁就不会产生死锁现象
        if(choice == 0){
            synchronized (lipstick){
                System.out.println("lipstick's lock " + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
            synchronized (mirror){
                System.out.println("mirror's lock " + Thread.currentThread().getName());
            }
        }else{
            synchronized (mirror){
                System.out.println("mirror's lock " + Thread.currentThread().getName());
                Thread.sleep(2000);
            }
            synchronized (lipstick){
                System.out.println("lipstick's lock " + Thread.currentThread().getName());
            }
        }
    }
}

  • 死锁形成原因

image-20220402184043561

Lock 锁

  • 给线程加锁的方法
  • 实例
package learnSpace.highLevel;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/2
 */
public class TestLock {
    public static void main(String[] args) {
        TestClock2 t = new TestClock2();

        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}


class TestClock2 implements Runnable{

    int num = 10;

    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                lock.lock();    //线程加锁
                if(num > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(num --);
                }else{
                    break;
                }
            }finally {
                lock.unlock();
            }
        }
    }
}

和 synchronized 的差别

image-20220402184755337

线程协作

线程通信

  • 生产者和消费者共享一个资源

    image-20220402185106982

  • Java 提供的几种方法

    • wait 线程等待唤醒
    • notify 唤醒线程
  • 注意 :都是 Object 类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常

解决方式

管程法

image-20220402185454099

信号灯法

线程池

image-20220402185623348

posted @ 2022-04-02 18:58  Xuuxxi  阅读(19)  评论(0编辑  收藏  举报