多线程-01 多线程基础

一、多线程基础

1 多线程概述

1)进程的概念?
正在运行的程序。每个程序启动时都会拉起一个进程。
2)多进程的意义?
单进程只做一件事,多进程即做多件事。多进程的意义在于提高CPU的利用率。
3)什么是线程?
一个进程可以执行多条任务,每个任务可以看成一个线程。
线程:程序的执行单元,是程序使用CPU的基本单位。
4)多线程的意义?
提高应用程序的使用率,提高获取CPU使用权的概率。

2 多线程实现方式

查看JDK API 1.8 查看官方怎么创建线程

2.1继承Thread类,重写run方法。

子类的实例执行父类的start()方法,生成线程。
在这里插入图片描述
使用这种方法,我们起2个新的线程。
在这里插入图片描述
观察启动的线程,Thread-0,Thread-1,查看Thread源码。上图中,实例化一个Thread就能拿到线程名,优先级等信息就是通过下面的构造方法+init()方法实现的。
在这里插入图片描述
t1.run()执行普通方法的调用,t1.start()为什么就牛逼哄哄创建新线程了?
在这里插入图片描述
native start0();调用了一个本地方法。这个本地方法在初始化时就通过静态代码块进行了注册。这里的本地方法是通过C/C++来实现的,主要通过JVM启动一个线程。
在这里插入图片描述

2.2 实现Runable接口

在这里插入图片描述
根据文档写代码。这里线程命名只是为了测试run()方法调用速度,threadName有机会在2个子线程中拿到相同的值,复现线程不安全问题。当然对run()方法加锁synchronized即可解决线程安全问题(更优的办法是另外添加synchronized的处理数据方法,保证CPU操作的原子性),这里为了体现多线程争抢CPU时间片的特性不做赘述,希望引起看官对多线程和线程安全的理解,仅供参考。2.3节展示多线程线程安全问题的一般做法。

/**
 * 方式:二 实现Runnable接口
 * 步骤 1 创建线程实现runnable接口
 *     2 实现run方法
 *     3 创建自定义线程类的对象
 *     4 创建Thread类对象,并把自定义线程类对象作为构造参数传递
 * @author Administrator
 */
public class MyRunnableThread implements Runnable{
    private int threadName = 0;

    @Override
    public void run() {
     	this.threadName++;
        Thread.currentThread().setName("子线程" +threadName);
        for (int i =0;i<100;i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

/**
 * @author Administrator
 */
public class HeiMaTest2 {
    public static void main(String[] args) {
        MyRunnableThread r =new MyRunnableThread();

        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();

        Thread.currentThread().setName("主线程");
        for (int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" " + i);
        }
    }
}

Runnable接口很简单,只要实现run()方法,就能跑。
在这里插入图片描述

2.3 实现Runable接口和继承Thread类实现多线程的区别

1、Runnable可以解决java单继承局限性,例:某个类有父类无法再继承Thread类场景。
2、实现Runnable,可以复用MyRunnableThread 的代码,因为多个线程使用的是同一个r对象。[结合2.2节实例理解] —更适合数据线程共享。
3、多线程卖票代码实例[ps:同步代码块的锁对象是任意对象,同步方法的锁对象是this,静态方法的所对象是字节码文件sellTicket.class 反射]

/**
 * @author Administrator
 */
public class SellTicket implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0){
            sell();
        }
    }

    public synchronized void sell(){
        if (ticket>0){
            //每调用一次这个方法,总票数减少1张
            this.ticket--;
            int i = 100 - ticket;
            System.out.println(Thread.currentThread().getName() + "卖出第" + i + "一张票,目前余票:" + ticket);
        }
    }
}

2.4 JDK1.5后的Lock锁

3 线程调度及线程控制

3.1线程调度的两种模型

3.1.1 分时调度模型
平均分配CPU时间片
3.1.2 抢占式调度模型
优先级高的线程具有优先获取CPU时间片的几率。
在这里插入图片描述

3.2 线程控制

3.2.1 线程休眠
Thread.sleep(long millis);
3.2.2线程加入
public final void join(); 等待该线程消亡(其他线程等待)
在这里插入图片描述
3.2.3 线程礼让
public static void yield();暂停当前正在执行的线程对象,并执行其他线程。
在这里插入图片描述
3.2.4 后台线程 (守护线程— 当主线程结束时,守护线程也推退出)
在这里插入图片描述
跑了好几遍才跑出一个能说明问题的素材(守护线程均需要执行200次,这里在主线程结束时守护线程立即消亡)
在这里插入图片描述
3.2.5 线程终止
public final void stop(); 不安全,会影响其他线程的运行。
public void interrupt();interrupt方法用于中断线程,把线程状态修改,并抛出InterruptedException。

4 线程的生命周期?

新建:创建线程对象实例
就绪:有执行资格,没有CPU使用权
运行:有执行资格,有CPU执行权
阻塞:没有执行这个,没有执行权。激活后拥有执行资格,处于就绪状态
死亡 :线程对象变成垃圾,等待被回收
在这里插入图片描述

二、多线程应用

1、多使用局部对象(局部临时变量),而不是全局变量
2、使用不可变类(final),一旦创建,其值不再可变,降低代码中需要同步的数量。
3、尽量不适用上锁的代码
4、使用线程池的Excutor,而不是new Thread
5、使用BlockingQueue实现生产-消费模式,使用队列消减浪涌
6、避免使用静态变量,必要是使用final
7、即使使用Lock.lock(),也不用同步synachronized

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旧梦昂志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值