多线程---上

什么是程序?什么是进程?什么是线程?
  • 程序:一组静态代码

  • 进程:正在进行着的一组程序

  • 线程:进程中的一个单元

主要学习多线程使用

一个线程运行状态

在这里插入图片描述

  • 创建状态: new 一个线程对象
  • 就绪状态:调用对象的start方法
  • 运行状态: 一个线程的运行是由系统(cpu)分配的,不是自己可控制的
  • 阻塞状态:当一个线程,进行某些耗时操作时,会让出cpu资源,进入阻塞状态,重新进入就绪状态,等待cpu分配资源
  • 死亡状态:当线程遇到异常或程序执行完毕,会进入死亡状态
如何使用线程

方式一(继承Thread) :

  • 创建线程类,继承Thread
  • 重写run方法
  • 创建线程对象,调用start方法
public class RunnerMan extends Thread {
    private String name;
    public RunnerMan(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 101; i++) {
            System.out.println(this.name+"跑了  "+i+"  米");
        }
    }
}
// =----------------------------------------
public class Main {
    public static void main(String[] args) {
        RunnerMan r1 = new RunnerMan("苏炳添");
        RunnerMan r2 = new RunnerMan("博尔特");
        RunnerMan r3 = new RunnerMan("加特林");
        r1.start();
        r2.start();
        r3.start();
    }
}

方式二(实现Runnable接口,为了解决不能多继承的问题):

  • 创建线程类,实现Runnable

  • 重写run方法

  • 创建线程对象,创建Thread对象 ,然后把此对象作为Thread对象参数

  • 调用Thread对象的start方法

    public class RunnerMan implements Runnable {
        private String name;
        public RunnerMan(String name){
            this.name = name;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 101; i++) {
                System.out.println(this.name+"跑了  "+i+"  米");
            }
        }
    }
    // ----------------------------------
    public class Main {
        public static void main(String[] args) {
            RunnerMan r1 = new RunnerMan("苏炳添");
            RunnerMan r2 = new RunnerMan("博尔特");
            RunnerMan r3 = new RunnerMan("加特林");
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);
            Thread t3 = new Thread(r3);
            t1.start();
            t2.start();
            t3.start();
    
        }
    }
    
    
  • 售票小练习

    public class Main {
        public static void main(String[] args) {
            // 做小练习:抢票系统
            // 分析: Ticket类 System12306类 Window类
            Window w1 = new Window("北京南站");
            Window w2 = new Window("北京西站");
            Window w3 = new Window("北京东站");
            w1.start();
            w2.start();
            w3.start();
    
        }
    }
    // -----------------------------
    /*
    * 这是一javaBean对象,专门用来存储数据,又叫做pojo(简单的java对象)
    * */
    public class Ticket {
        private String startStation;
        private String stopStation;
        private Double price;
    
        public Ticket() {
        }
    
        public Ticket(String startStation, String stopStation, Double price) {
            this.startStation = startStation;
            this.stopStation = stopStation;
            this.price = price;
        }
    
        public String getStartStation() {
            return startStation;
        }
    
        public String getStopStation() {
            return stopStation;
        }
    
        public Double getPrice() {
            return price;
        }
    
        public void setStartStation(String startStation) {
            this.startStation = startStation;
        }
    
        public void setStopStation(String stopStation) {
            this.stopStation = stopStation;
        }
    
        public void setPrice(Double price) {
            this.price = price;
        }
    }
    
    // -----------------------------
    import java.util.Vector;
    
    public class System12306 {
    
        // 由于系统是由几个窗口通用的,所以需要使用单例模式
        private System12306(){};
        private static System12306 system = new System12306();
        public static System12306 getInstance(){
            return system;
        }
        private Vector<Ticket> tickets = new Vector<>();
        // 由于Vector 线程安全的,这就保证了在同一时间,只存在一个操作
    
        // 创建100 张 票
        // 静态代码块,在创建对象之前,通常用于初始化
        {
            for (int i = 1; i < 101; i++) {
                tickets.add(new Ticket("北京"+i,"海南"+i,(i+100)/5*0.6));
            }
        }
        // 创建取票方法
        public Ticket getTicket(){
            try{
                return tickets.remove(0);
            }catch(Exception e){
                return null;
            }
        }
    }
    
    // -----------------------------
    public class Window extends Thread{
        // 窗口使用,多线程并发,进行售票,公用一个系统
        private String stationName;
        public Window(String stationName){
            this.stationName = stationName;
        }
        // 重写run方法
    
    
        @Override
        public void run() {
    
            sellTicket();
        }
    
        private void sellTicket() {
            System12306 system = System12306.getInstance();
            while (true){
                Ticket ticket = system.getTicket();
                if(ticket == null){
                    System.out.println("没有票了");
                    return;
                }
                System.out.println(this.stationName+"  ["+ticket.getStartStation()+" ---- "+ticket.getStopStation()+"  :"+ticket.getPrice()+"]");
            }
    
        }
    }
    // -----------------------------
    
存在的问题

问题一:现在所使用的Vector,是线程同步的,如何自定义线程非同步的容器

问题二: 如何进行各线程状态之间的切换

用一个例子:生产者消费者模型

public class Main {
    public static void main(String[] args) {
        // 生产者消费者模型
        Producer p1 = new Producer();
        Consumer c1 = new Consumer();
        Consumer c2 = new Consumer();
        p1.start();
        c1.start();
        c2.start();

    }
}
// -----------------------------
import java.util.ArrayList;

public class Warehouse {

    // 单例模式
    private Warehouse(){};
    private static Warehouse warehouse = new Warehouse();
    public static Warehouse getInstance(){
        return warehouse;
    }
    private ArrayList<String> wares = new ArrayList<String>();
    // 用于生产者调用
    public void add() {
        if(wares.size()<10){
            wares.add("a");
        }else{
            return;
        }
    }
    // 用于消费者调用
    public void get(){
        if(wares.size()>0){
            String remove = wares.remove(0);
        }else{
            return;
        }
    }
}
// -----------------------------
public class Producer extends Thread {
    public Warehouse warehouse = Warehouse.getInstance();
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.warehouse.add();
            System.out.println("生产一个包子");
        }
    }
}


// -----------------------------
public class Consumer extends Thread{
    public Warehouse warehouse = Warehouse.getInstance();
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.warehouse.get();
            System.out.println("消费一个包子");
        }
    }
}

// -----------------------------
// 这样会报错,ArrayIndexOutOfBoundsException
// why?
// 通过这个模型成功的演示了线程安全问题
// 两个消费者,同时访问同一个仓库对象,并发访问,产生抢占资源问题,导致ArrayIndexOutOfBoundsException
// 解决方案: 加锁
如何进行加锁?

特征修饰符:synchronized

线程安全锁 两种方式

  • 方式1:将synchronized关键字,放在方法的结构上

    public synchronized void get(){}

    锁定的是调用方法时的那个对象,在这里是消费者

  • 方式2:将synchronzied关键字 放在方法(构造方法 普通方法 块)内部

    public void get(){

    ​ 好多代码

    ​ synchronzied(对象){

    ​ 操作异步的代码;

    ​ }

    ​ 好多代码

    }

各种状态之间的切换

// -----------------------------
import java.util.ArrayList;

public class Warehouse {

    // 单例模式
    private Warehouse(){};
    private static Warehouse warehouse = new Warehouse();
    public static Warehouse getInstance(){
        return warehouse;
    }
    private ArrayList<String> wares = new ArrayList<String>();
    // 用于生产者调用
    public synchronized void add() {
        if(wares.size()<10){
            wares.add("a");
        }else{
            try {
                this.notifyAll();
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    // 用于消费者调用
    public synchronized void get(){
        if(wares.size()>0){
            String remove = wares.remove(0);
        }else{
            try {
                this.notifyAll();
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
  }
}
// 其他类中的代码不变
如果没有线程锁,导致的问题
  • ArrayIndexOutOfBoundsException :数组越界异常
  • IllegalMonitorStateException: 不合法监视异常
笔试题
  • 程序 进程 线程 之间的区别

  • 线程的创建方式

  • 线程的几种状态之间的切换

  • sleep方法 和 wait方法之间的区别

    区别点sleepwait
    ThreadObject
    调用静态方法任何对象
    理解哪个位置调用,哪个线程等待访问对象的其他线程等待
    唤醒不需要其他对象唤醒(notify| notifyAll)
    不会释放锁等待后会释放锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值