Java多线程笔记【一】

一、基本概念

  • 程序(program):是为了完成特定任务、使用某种语言编写的一组指令的集合——一段静态的代码,静态对象

  • 进程(Process):一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程,进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序是一个进程从创建运行到消亡的过程。进程具有:独立性,动态性,并发性

    • 独立性:进程时一个能独立运行得基本单位,同时也是系统分配资源和调度得独立单位
    • 动态性:进程的实质时程序的一次执行过程,进程是动态产生,动态消亡的。
    • 并发性:任何进程都可以同其他进程一起并发执行
    • 进程作为资源分配的单元,系统在运行的时候会给每一个进程分配不同的内存区域
  • 线程(Thread):进程中一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称为多线程程序

    • 单线程:一个进程如果只有一条执行路径,则称为单线程程序
    • 多线程:一个进程如果有多条执行路径,则称为多线程程序
    • 线程作为调度和执行的单位,每个线程都拥有独立的运行栈和计数器(PC)
    • 一个进程中的多个线程是共享 堆和方法区的。
  • 并发:一个CPU(采用时间片)同时执行多个任务 比如:多个人做通一件事

  • 并行:多个CPU同时直接多个任务 比如:多个人同时做不同的事情

  • 单核CPU:一次只能执行一个线程,CPU按时间片执行,同一个时间片只能执行一个线程。时间片切换快,会造成和多线程一样执行的假象。实际是单线程,一个时间只执行一个线程。

  • 多核CPU:多个CPU可同时执行多个线程。真正意义上一个时间片多线程同时执行。

二、线程

2.1 线程的创建和使用

  • Java的多线程是通过java.lang.Thread类来体现的
  • 创建线程有俩种方法
    • 继承Thread类
    • 实现Runnable接口
    • 实现Callable接口

2.1.1 Thread类

2.1.1.1 特性
  • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
  • 通过该Thread对象的start()方法来启动这个线程,run是赋值封装被线程执行的代码,直接调用相对于普通方法,并没有开启线程
  • 开启线程的是start()方法,先启动线程,然后由JVM调用此线程的run()方法,线程开启不一定立即执行,由cpu调度执行
2.1.1.2 构造器
  • Thread():创建新的Thread对象
  • Thread(String threadname):创建线程并指定线程实例名
  • Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
  • **Thread(Runnable target, String name):**创建新的Thread对象
2.1.1.3 Thread的常用方法
  • void start(): 启动线程,并执行对象的run()方法
  • run(): 线程在被调度时执行的操作
  • String getName(): 返回线程的名称
    • 注:线程由默认名字,格式:Thread-编号
  • void setName(String name):设置该线程名称
    • 通过构造方法也可以设置线程名字,需要再类中调用super
public class MyThread extends Thread {

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

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


public class ThreadMethodDemo1 {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("小白");
        MyThread mt2 = new MyThread("小衣");


        mt1.start();
        mt2.start();
    }
}
  • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
public class MyThread extends Thread {
	@Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "=" +i);
        }
    }
}


public class ThreadMethodDemo1 {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();

        mt1.start();
        mt2.start();
    }
}
  • static void yield():暂停当前正在执行的线程对象,并执行其他线程
    • 将线程从运行状态转为就绪状态
    • 让cpu重新调度,礼让不一定成功!
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

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


class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行");
        Thread.yield();  // 礼让
        System.out.println(Thread.currentThread().getName() + "线程停止执行");
    }
}
  • public static void sleep(long time):让线程休眠指定的时间,单位为毫秒
    • sleep(时间)指定当前线程阻塞的毫秒数;
    • 抛出InterruptedException异常
    • sleep时间借宿后线程重新进入到就绪状态
public class SleepDemo1 {
    public static void main(String[] args) throws InterruptedException {

        MyRunnable myRunnable = new MyRunnable();

        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);

        t1.start();
        t2.start();
    }
}


public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + "-" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • public final void join():等待这个线程死亡。 (可以理解成插队)
    • join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程Vip来了");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();

        Thread t1 = new Thread(testJoin);
        t1.start();

        for (int i = 0; i < 1000; i++) {
            if (i==200){
                t1.join(); // 插队
            }
            System.out.println("main" + i);
        }
    }
}
  • public static boolean interrupted():中断线程,不推荐使用

  • stop():强制线程生命期结束,不推荐使用

    • 推荐让线程自动停止
  • boolean isAlive():返回boolean,判断线程是否还活着

2.1.1.4 创建线程——Thread
  • 定义一个类继承Thread

  • 在类中重写run()方法

  • 创建对象

  • 调用start()方法启动线程,由线程调用run方法

    public class Demo1 {
        public static void main(String[] args) {
            // 创建线程对象
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
    
            // 开启线程
            t1.start();
            t2.start();
        }
    }
    public class MyThread extends Thread {
    
        @Override
        public void run(){
            // 代码就是线程在开启之后执行的代码
            for (int i = 0; i < 100; i++) {
                System.out.println("线程启动了" + i);
            }
        }
    }
    

注意事项

  • run是赋值封装被线程执行的代码,直接调用相对于普通方法,并没有开启线程
  • 开启线程的是start()方法:1.先启动线程2.然后由JVM调用此线程的run()方法
  • 线程开启不一定立即执行,由cpu调度执行
  • 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
2.1.1.5 创建线程——Runnable接口
  • 定义一个类RunnableTest实现Runnable接口

  • 在类中重写run()方法

  • 创建RunnableTest对象

  • 创建Thread类的对象,把RunnableTest对象作为构造方法的参数

  • 调用start()方法启动线程

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
        }
    }
    public class RunnableDemo1 {
        public static void main(String[] args) {
            // 创建了一个参数的对象
            MyRunnable myRunnable = new MyRunnable();
    
            // 创建线程对象,把参数传递进去
            // 线程启动后调用的是参数的run方法
            Thread thread = new Thread(myRunnable);
    
            thread.start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("main" + i);
            }
        }
    }
    
2.1.1.6 创建线程——Callable和Future接口
  • 定义一个类MyCallable实现Callable接口
  • 在MyCallable类中重写call()方法
  • 创建Mycallable类的对象
  • 创建Future的来实现类FutureTask对象,把MyCallable对象作为构造方法的参数
  • 创建Thread类的对象,把FutureTask对象作为构造方法的参数
  • 启动线程

Future接口

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
  • FutrueTask是Futrue接口的唯一的实现类
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {

        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
        // 返回指表示线程运行完毕之后的结果
        return "答应";
    }
}
public class CallableDemo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 线程开启之后需要执行里面的call方法
        MyCallable mc = new MyCallable();
        // 可以获取线程完毕之后的结果,也可以作为参数传递给Thread对象
        FutureTask<String> ft = new FutureTask<>(mc);
        // 创建线程对象
        Thread t1 = new Thread(ft);
        // 开启线程
        t1.start();
        String s = ft.get();
        System.out.println(s);
    }
}

注意事项:

  • FutureTask中的get方法要字线程启动之后才能调用,get可以获取线程执行后的结果
2.1.1.7 三种方式

区别:

  • 继承Thread:线程代码存放Thread子类run方法中。
  • 实现Runnable:线程代码存在接口的子类的run方法。
  • 实现Callable接口:线程代码存在接口的子类的call方法。

实现的好处好处:

  • 避免了单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

Runnable与Callable相比较:

  • 相比run()方法,可以有返回值
  • 方法可以抛出异常
  • 支持泛型的返回值
  • 需要借助FutureTask类,比如获取返回结果

2.1.2 线程调度

多线程的并发运行:计算机的CPU,再任意时刻只能执行一条机器指令。每个线程只有获取CPU的使用权才能执行代码,各个线程轮流获得CPU的试用期,分别执行各自的任务。

调度模式

  • 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
  • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取CPU时间片相对多一些
  • 时间片策略:同优先级线程组成先进先出队列(先到先服务)
2.1.2.1 线程的优先级

**优先级范围:1~10,默认值是:5,优先级越高,抢到时间片几率越高 **

  • MAX_PRIORITY:10
  • MIN _PRIORITY:1
  • NORM_PRIORITY:5

优先级的方法:

  • public final void setPriority(int newPriority):设置线程的优先级
  • public final int getPriority():获取线程的优先级z

注意:

  1. 线程创建时继承父线程的优先级
  2. 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
  3. 线程优先级的设置最好再 start() 之前。
package state;

// 测试线程的优先级
public class TestPriority {

    public static void main(String[] args) {
        // 主线默认优先级
        System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread thread = new Thread(myPriority);
        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        // 先设置优先级
        thread.start();


        thread1.setPriority(1);
        thread1.start();

        thread2.setPriority(4);
        thread2.start();

        thread3.setPriority(Thread.MAX_PRIORITY); // MAX_PRIORITY=10
        thread3.start();

    }
}



class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猩空中的猩⭐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值