Skip to content

Latest commit

 

History

History
404 lines (324 loc) · 10.4 KB

1_多线程.md

File metadata and controls

404 lines (324 loc) · 10.4 KB

Java 多线程

  • 主要内容来自b站-狂神说

1. 基本概念

  • 程序-Process:指令和数据的有序集合,是一个静态的概念
  • 进程-Thread:执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
  • 线程:CPU调度和执行的单位,通常一个进程包含多个线程
    • 独立的执行路径
    • 如果开辟了多个线程,则线程的调度由调度器(cpu)安排调度
    • 线程会带来额外的开销,如:cpu调度时间、并发控制开销

2.线程的创建

2.1 overview

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

2.2 实例

2.3 静态代理

  • 优点:
    • 代理对象可以帮助真实对象做其他重复性的工作
    • 真实对象只需要专注做自己的工作即可
  • 静态代理总结
    • 真实对象和代理对象的要实现同一个接口
    • 代理对象要代理真实对象
  • 实例
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
        new WeddingCompany(you).HappyMarry();
        //类似多线程
        new Thread(()->System.out.println("run...")).start();
    }

}

// 定义都要实现的接口
interface Marry{
    void HappyMarry();
}

// 真实对象
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("我要结婚了!!!");
    }
}

// 代理对象
class WeddingCompany implements Marry{
    private Marry target;
    WeddingCompany(Marry target){
        this.target=target;
    }
    @Override
    public void HappyMarry() {
        before();
        target.HappyMarry();
        after();
    }

    private void before() {
        System.out.println("结婚之前,布置现场");
    }

    private void after() {
        System.out.println("结婚之后,清理现场");
    }
}

3.线程的状态

3.1 线程常用操作

4. 线程同步

  • 并发:同一个对象被多个线程同时操作
  • 线程同步:一种等待机制
    • 当多个线程访问同一个对象,并且一些线程还要修改这个对象时,需要线程同步
    • 队列+锁
  • 由来:由于同一个进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了冲突,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待其释放后才可以使用该对象
  • 问题:
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起
    • 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

  • 代码示例
package syn;

// 使用多线程不安全的买票
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicker buyTicker = new BuyTicker();
        new Thread(buyTicker,"老师1").start();
        new Thread(buyTicker,"老师2").start();
        new Thread(buyTicker,"老师3").start();
        new Thread(buyTicker,"老师4").start();

    }
}

class BuyTicker implements Runnable{
    private int ticketNums = 10;
    boolean flag = true;
    // synchronized同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        if(ticketNums<=0){
            flag=false;
            return;
        }
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }

    @Override
    public void run() {
        while(this.flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package syn;

public class UnsafeBank {
    public static void main(String[] args) {
        Accout accout = new Accout(400,"百度银行卡");
        new Drawing(accout,300,"小红").start();
        new Drawing(accout,300,"小李").start();
        new Drawing(accout,300,"小哥").start();
    }
}

// 账户类
class Accout{
    int money;
    String name;
    public Accout(int money,String name){
        this.money=money;
        this.name = name;
    }
}

// 银行模拟取钱
class Drawing extends Thread{
    Accout accout;
    // 要去的钱
    int drawingMoney;
    //现在手里面的钱
    int nowMoney;
    public Drawing(Accout accout,int drawingMoney,String name){
        super(name);
        this.accout=accout;
        this.drawingMoney=drawingMoney;
    }
    @Override
    public void run() {
        // 锁的对象就是变化的量
        synchronized (accout){
            if(accout.money-drawingMoney<0){
                System.out.println(this.getName()+"钱不够,取不了");
                return;
            }
            // sleep加大问题发生的可能性
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 卡内余额
            accout.money = accout.money-drawingMoney;
            nowMoney=nowMoney+drawingMoney;
            System.out.println(accout.name+"余额为:"+accout.money);
            System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }

    }
}
// 线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }

            }).start();
        }
        Thread.sleep(100);
        System.out.println(list.size());
    }
}

5.死锁

  • 多个线程各自占有一些共享资源,并且相互等待其他线程占有资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况
  • 某个同步块同时拥有两个以上的对象锁时,就可能发生死锁问题

package syn;

// 死锁:多个线程相互抱着对方需要的资源,然后形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup m1 = new Makeup(0,"女孩1");
        Makeup m2 = new Makeup(1,"女孩2");
        m1.start();
        m2.start();
    }
}

// 口红
class Lipstick{

}
// 镜子
class Mirror{

}

class Makeup extends Thread{
    // 需要的资源只有一份,用static保证
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//使用化妆品的人
    public Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    // 化妆
    private void makeup() throws InterruptedException {
        if(choice==0){
            //获得口红的锁
            synchronized (lipstick){
                System.out.println(this.girlName+"获得口红");
                Thread.sleep(1000);
//                synchronized (mirror){
//                    System.out.println(this.girlName+"获的镜子");
//                }

            }
            synchronized (mirror){
                System.out.println(this.girlName+"获的镜子");
            }
        }else{
            //获得镜子的锁
            synchronized (mirror){
                System.out.println(this.girlName+"获得镜子");
                Thread.sleep(2000);
//                synchronized (lipstick){
//                    System.out.println(this.girlName+"获得口红");
//                }

            }
            synchronized (lipstick){
                System.out.println(this.girlName+"获得口红");
            }
        }
    }
    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

6.锁

// 使用多线程不安全的买票
public class UnsafeBuyTicketUseLock {
    public static void main(String[] args) {
        BuyTicker1 buyTicker = new BuyTicker1();
        new Thread(buyTicker,"老师1").start();
        new Thread(buyTicker,"老师2").start();
        new Thread(buyTicker,"老师3").start();
        new Thread(buyTicker,"老师4").start();

    }
}

class BuyTicker1 implements Runnable{
    private int ticketNums = 10;
    boolean flag = true;
    // 定义锁
    private final ReentrantLock lock = new ReentrantLock();

    // synchronized同步方法,锁的是this
    private void buy() throws InterruptedException {
        try{
            lock.lock();
            if(ticketNums<=0){
                flag=false;
                return;
            }
            Thread.sleep(100);
            //买票
            System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);

        }finally {
            lock.unlock();
        }
    }

    @Override
    public void run() {
        while(this.flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

7.生产者消费者问题

  • 引出线程通信的必要性

8.线程池