笔记(课14)(1)

一阶段:第14天:多线程基础(8.10)

一.进程和线程

1.1进程(Process)

  1. 正在运行的程序,是一个程序的运行状态和资源占用(内存,CPU)的描述,通过进程ID区分。
  2. 进程是程序的一个动态过程,它指的是从代码加载到执行完毕的一个完成过程。
  3. 目前操作系统支持多进程多任务。
  4. 进程的特点:
    a.独立性:不同的进程之间是独立的,相互之间资源不共享
    b.动态性:进程在系统中不是静止不动的,而是在系统中一直活动的
    c.并发性:多个进程可以在单个处理器上同时进行,且互不影响

1.2线程

  1. 对于每个线程,栈空间是独立的,堆空间是共享的
  2. 线程就是一条执行路径。是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务。
  3. 线程的特点:线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是CPU快速的在不同的线程之间切换,也就是说,当前运行的线程在任何时候都有可能被挂起,以便另外一个线程可以运行。

1.3线程和进程的联系与区别

a.一个程序运行后至少有一个进程
b.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的
c.进程间不能共享资源,但线程之间可以
d.系统创建进程需要为该进程重新分配系统资源,而创建线程则容易的多,因此使用线程实现多任务并发比多进程的效率高

 * java程序运行时,会自动创建主线程,执行main方法
 * 在主线程(main)创建的线程是子线程
 * 虽然子线程是在主线程中创建的,但是start()后他们就是分别独立的线程了
 *
 * 线程通过线程id和名称区分---推荐使用第二种
 *1super.getId() super.getName();
 *2)Thread.currentThread.getId() Thread.currentThread.getName()
 * 
public class Demo1 {
    public static void main(String[] args) {
        //创建线程对象
        MyThread myThread=new MyThread("少泊");
        myThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getId()+" "+Thread.currentThread().getName()+"主线程----"+i);
        }
    }
}
public class MyThread extends Thread{
    public MyThread(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(super.getId()+" "+super.getName()+"子线程---"+i);
        }
    }
}

二.多线程的实现

2.1继承Thread

Thread类是所有线程类的父类,实现了对线程的抽取和封装
继承Thread类创建并启动多线程的步骤:
a.定义一个类,继承自Thread类,并重写该类的run方法,该run方法的方法体就代表了线程需要完成的任务,因此,run方法的方法体被称为线程执行体
b.创建Thread子类的对象,即创建了子线程
c.用线程对象的start方法来启动该线程

public class TicketWin extends Thread{
    private int ticket=100;
    public TicketWin(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getId()+Thread.currentThread().getName()+"卖了"+(i+1)+"张票");
            ticket--;
        }
    }
}
// 4个窗口卖票
public class Demo2 {
    public static void main(String[] args) {
        TicketWin ticketWin=new TicketWin("窗口1");
        TicketWin ticketWin1=new TicketWin("窗口2");
        TicketWin ticketWin2=new TicketWin("窗口3");
        TicketWin ticketWin3=new TicketWin("窗口4");
        ticketWin.start();
        ticketWin1.start();
        ticketWin2.start();
        ticketWin3.start();
        //不要写成tickWin.run();这样就跟线程无关了
    }
}

2.2实现Runnable接口

每个线程.start()时都会创建一个新的栈空间,然后运行线程.run(),都去调用堆空间中的数据
实现Runnable接口创建并启动多线程的步骤:
a.定义一个Runnable接口的实现类,并重写该接口中的run方法,该run方法的方法体同样是该线程的线程执行体
b.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
c.调用线程对象的start方法来启动该线程

public class Ticket implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while (true){
            if (ticket<1){
                break;
            }
            System.out.println(Thread.currentThread().getName()+" 卖了"+(100-ticket+1)+"张票");
            ticket--;
        }
    }
}
 * 案例:4个窗口共卖100张票
public class Demo1 {
    public static void main(String[] args) {
        /*//1.创建票对象
        Ticket ticket=new Ticket();
        //2.创建线程对象---卖票窗口
        Thread w1=new Thread(ticket, "窗口1");
        Thread w2=new Thread(ticket, "窗口2");
        Thread w3=new Thread(ticket, "窗口3");
        Thread w4=new Thread(ticket, "窗口4");
        //启动线程
        w1.start();
        w2.start();
        w3.start();
        w4.start();*/

        //代码简化
        Runnable ticket=new Runnable() {
            @Override
            public void run() {
                int ticket=100;
                while (true){
                    if (ticket<1){
                        break;
                    }
                    System.out.println(Thread.currentThread().getName()+" 卖了"+(100-ticket+1)+"张票");
                    ticket--;
                }
            }
        };
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();
    }
}

2.2.1综合案例

// 案例:银行卡
public class Demo2 {
    public static void main(String[] args) {
        //1.创建银行卡
        BankCard card=new BankCard();
        //2.创建存钱和取钱功能
        AddMoney addMoney=new AddMoney(card);
        SubMoney subMoney=new SubMoney(card);
        //3.创建线程对象
        Thread shaobo=new Thread(addMoney,"少泊");
        Thread xiaohai=new Thread(subMoney,"小海");
        //4.启动
        shaobo.start();
        xiaohai.start();
        //简化代码
        BankCard card1=new BankCard();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    card.setMoney(card.getMoney()+1000);
                    System.out.println("存了1000,余额是"+card.getMoney());
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    if (card.getMoney()>=1000){
                        card.setMoney(card.getMoney()-1000);
                        System.out.println("取了1000,余额是"+card.getMoney());
                    }else {
                        System.out.println("余额不足,及时存钱");
                        i--;
                    }
                }
            }
        }).start();
    }
}
public class BankCard{
    private double money;
    public BankCard() {
    }
    public BankCard(double money) {
        this.money = money;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
}
public class AddMoney implements Runnable{
    private BankCard card;
    public AddMoney(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        //存钱
        for (int i = 0; i < 10; i++) {
            card.setMoney(card.getMoney()+1000);
            System.out.println(Thread.currentThread().getName()+"存了1000元,余额为"+card.getMoney());
        }
    }
}
public class SubMoney implements Runnable{
    private BankCard card;
    public SubMoney(BankCard card) {
        this.card = card;
    }
    public BankCard getCard() {
        return card;
    }
    public void setCard(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (card.getMoney()>=1000){
                //取钱
                card.setMoney(card.getMoney()-1000);
                System.out.println(Thread.currentThread().getName()+"取了1000,余额为"+card.getMoney());
            }else{
                System.out.println("余额不足,及时存钱");
                i--;
            }
        }
    }
}

2.3 比较Thread和Runnable

  1. 继承Thread类的方式
    a.没有资源共享,编写简单
    如果要访问当前线程,除了可以通过Thread.currentThread()方式之外,还可以使用getName()获取线程名字。
    b.弊端:因为线程类已经继承了Thread类,则不能再继承其他类【单继承】
  2. 实现Runnable接口的方式
    a.可以多个线程共享同一个资源,所以非常适合多个线程来处理同一份资源的情况
    b.资源类实现了Runnable接口。如果资源类有多个操作,需要把功能提出来,单独实现Runnable接口。
    c.弊端:编程稍微复杂,不直观,如果要访问当前线程,必须使用Thread.currentThread()

总结:实际上大多数的多线程应用都可以采用实现Runnable接口的方式来实现【推荐使用匿名内部类】

2.4 Callable接口实现多线程

public class Demo3 {
    public static void main(String[] args) throws Exception{
        //1.创建可调用对象
        MyCallable callable=new MyCallable();
        //把 callable变成一个任务
        FutureTask<Integer> task=new FutureTask<>(callable);
        //2.创建线程
        //传递任务可以,不能传递callable
        Thread thread=new Thread(task);
        //3.启动
        thread.start();
        //获取结果
        Integer sum=task.get();//sleep 会阻塞,直到进程结束返回结果
        System.out.println("结果是:"+sum);
    }
}
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {

        int sum=0;
        for (int i = 0; i <= 100; i++) {
            Thread.sleep(100);
            sum+=i;
            System.out.println(i);
        }
        return sum;
    }
}

三.线程的常用方法

线程对象.setName(); //设置线程名
Thread.currentThread().getName();//获取当前线程的线程名

3.1线程休眠

使得当前正在执行的线程休眠一段时间,释放时间片,导致线程进入阻塞状态(单位毫秒)。跟线程的优先级无关,当对应的时间到了之后,还会再继续执行。

public class Demo1 {
    public static void main(String[] args) {
        SleepThread sleepThread=new SleepThread();
        Thread thread=new Thread(sleepThread);
        thread.start();
    }
}
public class SleepThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"..."+i);
            try {
                //如果想让程序运行快点,可以适当调整大小
                Thread.sleep(10000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

3.2线程优先级

默认情况下,每个线程的优先级都与创建它的父线程具有相同的优先级,例如:main线程具有普通优先级,则由main线程创建的子线程也有相同的普通优先级。
优先级范围1~10,默认为5,对应的数值越大,说明优先级越高,这个方法的设置一定要在start之前。
线程的优先级低并不意味着争抢不到时间片,只是抢到时间片的概率比较低而已。

 p1.setPriority(1);
 p3.setPriority(10);

3.3合并(加入)线程(join)

优先执行合并进来的线程,join之前,一定要将线程处于准备状态start

public class Demo3 {
    public static void main(String[] args) /*throws Exception*/{
        JoinThread joinThread=new JoinThread();
        joinThread.start();
        //加入线程,阻塞当前线程(main),直到加入线程(joinThread)执行完毕,才继续执行
        try {
            joinThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

3.4后台线程

后台线程:隐藏起来一直在默默运行的线程,直到进程结束,又被称为守护线程,JVM的垃圾回收线程就是典型的后台线程。特征:如果所有的前台线程都死亡,后台线程会自动死亡。
前台线程:默认的线程都是前台线程,如果前台不执行完毕,程序不会退出。

   设置为后台线程
      daemonThread.setDaemon(true);
      daemonThread.start();

3.5线程让步

可以让当前正在执行的线程暂停,但它不会阻塞该线程,他只是将该线程转入就绪状态,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程才会获得执行的机会。完全可能出现的情况是:当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行

Thread.yield();

3.6线程中断 interrupt()

程序在等待过程中,可以使用interrupt方法打断。
所有能抛出InterruptedException异常的,都可以被打断。
interrupt是打断,stop是打死

四.生命周期

五个状态:
1.新生—>2.就绪---->3.运行(让步回2.)---->(4.阻塞—打醒或休眠结束回2.)----->5.死亡

  1. 新生:start()方法之前
  2. 就绪:start()
  3. 运行:抢到cup
  4. 阻塞:sleep(),wait(),join()
  5. 死亡:run()结束或stop()

七状态: 将阻塞细分解成3种情况
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值