Java多线程基础

这一部分的内容主要是为了明年的笔试面试做准备,将一些基础知识整理归纳,如果有不正确的地方欢迎指正

Java多线程

实现线程的两种主要方式

  1. 继承Thread类
  2. 实现Runnable接口

两者都是通过重写run方法完成相应的业务操作,需要注意的是线程的调用需要使用start方法,不能直接调用run方法,直接调用run方法时和调用普通的对象方法没有区别


public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}

class MyThread extends Thread{
    private String name;

    public MyThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}

上面的代码结果会发现线程2的id和主线程id相同,因为直接调用run方法时,是主线程调用了普通对象thread2的实例方法,在该run方法运行结束之前,其他的线程无法并发执行

此外使用继承Thread类的方式创建线程对象,多个线程对象之间是没有办法进行实例变量的共享的。而实现Runnable接口的方式创建对象作为Thread类对象的target这种方式,可以让多个线程对象共享成员变量,因为Java不能实现多继承,所以实现Runnable接口来创建多线程对象是一个不错的选择

使用Callable和hi是Future方式创建线程这边不讨论

线程的生命周期

线程的生命周期主要有新建,就绪,运行,阻塞,死亡这几个状态

  1. 新建:当一个线程对象被创建出来的时候就处于新建状态
  2. 就绪:当一个线程对象调用start方法的时候就处于就绪状态,但是并不一定会立即执行,需要等待cpu分配时间片,就绪表示的是线程可以执行了
  3. 如果一个就绪态的线程对象获得了CPU,开始或继续执行run方法,就处于运行态了,
    在下面的这些状况下,线程会放弃CPU,进入阻塞状态:
    • 线程调用sleep方法让出CPU
    • 调用了阻塞式IO方法返回之前线程会被阻塞
    • 当线程试图获取锁,但是锁被其他线程持有的时候,线程是处于阻塞状态的(等待锁)
    • 等待通知或suspend挂起线程(等待通知)

相应的就有从阻塞态进入就绪态的情况,注意这边是就绪态,并不是运行态。因为当线程从阻塞态恢复过来之后,能不能执行还是要看是有没有被分配到CPU时间片。

  1. 死亡:当线程执行体执行完毕,线程正常结束就进入死亡态,此外当线程抛出一个未捕获的Exception或者Error也会进入死亡态,直接调用线程的stop也会进入死亡状态,对于死亡的线程就不能调用start方法了

join线程

join方法,就是让一个线程等待另一个线程执行完毕,比如在main方法中调用了一个线程thread1的join方法,那么也就是说主线程在此时会进入阻塞状态,等待thread1执行完成


public static void main(String[] args){
     MyThread thread1 = new MyThread()
     thread1.start()
     thread1.join()
 }
public class JoinTest {
    public static void main(String[] args) throws InterruptedException{
        JoinThread t = new JoinThread("t");
        t.start();
        for(int i=0;i<20;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if(i==10){
                t.join();
            }
        }
    }
}

class JoinThread extends Thread{
    public JoinThread(String name){
        super(name);
    }
    public void run(){
        for(int i=0;i<20;i++){
            System.out.println(this.getName()+":"+i);
        }
    }
}
main:0
t:0
main:1
t:1
main:2
t:2
t:3
t:4
t:5
t:6
t:7
t:8
main:3
main:4
main:5
main:6
main:7
t:9
t:10
t:11
t:12
t:13
main:8
main:9
main:10
t:14
t:15
t:16
t:17
t:18
t:19
main:11
main:12
main:13
main:14
main:15
main:16
main:17
main:18
main:19

可以看到当主线程的i==10时调用t的join方法,主线程进入阻塞状态,直到线程t执行完成之后主线程才继续执行

后台线程

后台线程也叫守护线程,最常见的后台线程就是Java的GC线程,后台线程在所有的前台线程死亡时也会死亡
使用setDaemon方法可以将线程设置为守护线程,此外线程的创建有继承性,后台线程创建的子线程默认也是后台线程,前台线程创建的子线程默认也是前台线程,需要注意的是后台线程的设置必须在调用start方法之前

yield

线程让步,yield方法的作用是让当前的线程从运行状态转到就绪状态,和sleep方法有一点相似,但是sleep方法是将当前的线程状态变为阻塞,使用yield方法后线程进入就绪态,有可能又被分配到CPU时间片后执行,实际上只有与yield线程优先级相同或更高优先级的线程能够分配到CPU时间片

public class YieldTest {
    public static void main(String[] args){
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        // t1.setPriority(Thread.MAX_PRIORITY);
        // t2.setPriority(Thread.MIN_PRIORITY);
        t2.start();
        t1.start();
    }
}
class MyThread extends Thread{
    private String name;
    public MyThread(String name){
        super(name);
    }

    @Override
    public void run() {
        super.run();
        for(int i=0;i<5;i++){
            System.out.println(this.getName()+":"+i);
            if(i == 2)
                this.yield();
        }
    }
}
t2:0
t2:1
t2:2
t1:0
t1:1
t1:2
t2:3
t2:4
t1:3
t1:4

去掉注释后的执行结果

t1:0
t1:1
t1:2
t1:3
t2:0
t1:4
t2:1
t2:2
t2:3
t2:4

可以看到当不设置优先级的时候,两个线程是同一个优先级,当t2中i==2的时候t2变为让出CPU变成就绪态,而当设置了优先级的时候,t1中i==2的时候并没有让t2执行,仍然是t1获得了CPU时间片

多线程的同步和通信以及线程池的应用将在下面的文章中进行描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值