Java线程学习笔记

一、概念

进程和线程

​ 进程是程序的一次执行,是程序在内存的一个数据集合上运行的过程,是系统进行资源分配和调度的独立单元。每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。

​ 线程是进程中一个单一顺序的控制流,是操作系统进行运算调度的最小单位,包含在进程之中,进进程中的实际运作单位。一个进程可以并发多个线程,每个线程执行不同的任务

​ 线程自己基本不拥有系统资源,除了一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是可与属于同一进程的其他线程共享进程所拥有的系统资源

二、Java线程创建

Java中有三种创建线程的方式

1、继承Thread类

  • 定义Thread类的子类,并重写该类的run方法
  • 创建该子类的实例
  • 调用实例的start()方法来启动线程
public class MyThread extends Thread {

    @Override
    public void run() {
        for(int i=0;i<50;i++){
            if(i==29&&"Thread-0".equals(Thread.currentThread().getName())){
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
            }
            System.out.println(Thread.currentThread().getName()+"-"+i);
        }

    }

    public static void main(String[] args) {

        new MyThread().start();
        new MyThread().start();

    }
}

2、实现Runnable接口创建线程

  • 定义Runnable接口的实现类,并重写run方法
  • 创建实现类实例,并作为实例化Thread时的构造参数
  • 调用Thread实例的start方法来启动线程
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i=0;i<50;i++){
            if(i==29&&"Thread-0".equals(Thread.currentThread().getName())){
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
            }
            System.out.println(Thread.currentThread().getName()+"-"+i);
        }
    }

    public static void main(String[] args) {

        Runnable r = new MyRunnable();
        new Thread(r).start();
        new Thread(r).start();
    }
}

3、实现Callable接口来创建线程

  • 定义Callable接口的实现类,并重写call方法
  • 创建Callable实现类的实例,并作为实例化FutureTask的构造参数
  • 将FutureTask实例作为实例化Thread的构造参数
  • 调用Thread实例的start()方法来启动线程
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int ans = 0;
        for(int i=0;i<50;i++){
            if(i==29&&"Thread-0".equals(Thread.currentThread().getName())){
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
            }
            ans += i+1;
            System.out.println(Thread.currentThread().getName()+"-"+i);
        }
        return ans;
    }

    public static void main(String[] args) {
        Callable<Integer> c = new MyCallable();
        FutureTask<Integer> ft1 = new FutureTask<>(c);
        FutureTask<Integer> ft2 = new FutureTask<>(c);

        Thread t1 = new Thread(ft1);
        Thread t2 = new Thread(ft2);

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

        while(t1.getState() != Thread.State.TERMINATED
            ||t2.getState() != Thread.State.TERMINATED){

        }

        try {
            System.out.println(ft1.get() + "-"+ ft2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

三、线程创建的三种方式对比

1、继承Thread类

优点:编写简单,只需要继承Thread,重写run方法,并实例化即可启动线程,如果要访问当前的线程,不需要调用Thread.currentThread(),使用this即可

缺点:由于子类继承了Thread类,那就不能再继承其他类了

2、实现Callable和Runnable接口

优点:只是实现了接口,所以还可以继承其他类,除此之外,这种方式可以共享一个target对象,即可实现多个线程来处理同一份系统资源

缺点:编程稍微有点复杂,如果要访问当前线程,需要调用Thread.currentThread()

四、Callable和Runnable的区别

CallableRunnable
重写方法call()run()
返回值
异常可以抛出异常不可以抛出异常

五、Java线程的状态

通过java.lang.Thread.State枚举类的源码可以看出,Java线程有如下几个状态

public enum State {
        /**
         * 新建状态 实例化Thread后,启动之前的状态.
         */
        NEW,

        /**
         * 运行状态 分为就绪和正在运行.  线程启动之后,首先转化为就绪状态
         * 位于可运行线程池中,等待获取CPU使用权,转化为正在运行状态
         */
        RUNNABLE,

        /**
         * 阻塞状态 线程进入synchronized块或方法获取不到锁.
         * 或者调用对象的wait方法得到唤醒之后重新进入synchronized块或方法
         * 获取不到锁时转化为阻塞状态
         */
        BLOCKED,

        /**
         * 等待状态 调用以下方法后转化为等待状态.
         * Object#wait() Object.wait} with no timeout
         * #join() Thread.join} with no timeout
         * LockSupport#park() LockSupport.park
         *
         * 等待状态的线程需要另一个线程执行一些操作来唤醒当前线程
         * 
         * 比如, 一个线程调用了某个object的wait方法后,等待另一个线程
         * 调用该object的notify或者notifyAll方法来唤醒当前线程
         * 
         * 在当前线程调用某个线程的join方法后,等待这个线程执行完成后自动唤醒当前线程
         */
        WAITING,

        /**
         * 超时等待状态 用一个指定的时间参数调用以下方法后转化为超时等待状态.
         * sleep Thread.sleep
         * Object#wait(long) Object.wait} with timeout
         * #join(long) Thread.join} with timeout
         * LockSupport#parkNanos LockSupport.parkNanos
         * LockSupport#parkUntil LockSupport.parkUntil
         * 等待时间超过指定的时间后自动唤醒当前线程
         */
        TIMED_WAITING,

        /**
         * 终止状态 线程执行完成或者异常终止后的状态.
         */
        TERMINATED;
    }

Java线程的状态转化图如下

在这里插入图片描述

六、几个方法的比较

1、Thread#sleep(long):当前线程调用此方法,进入TIMED_WAITING状态,但是并不释放已获取的锁,指定的时间过后进入就绪状态

2、Thread#yield():当前线程调用此方法,立即交出CPU使用权,不释放锁资源,进入就绪状态,等待系统的调度,并不保证其他线程一定会拿到CPU的时间片,有可能当前线程进入就绪状态后又获取到CPU时间片,该方法与sleep方法类似,但是该方法达不到让步的目的。

3、thread#join()/join(long):当前线程中调用线程thread的join方法,当前线程进入WAITING或者TIMED_WAITING状态,且不会释放已获取的锁,直到线程thread执行完成或者时间到后,当前线程进入就绪状态。

4、object#wait()/wait(long):当前线程获取object对象锁后,调用object的wait方法,释放object对象锁,转化为TIMED_WAITING状态,进入等待队列,依靠其他线程调用该object对象的notify或者notifyAll来唤醒,或者是wait(long)时间到后自动唤醒当前线程

5、object#notify()/notifyAll():notify方法唤醒在此对象监控器上等待的单个线程,具有单一任意性,notifyAll方法唤醒在此对象监控器上等待的所有线程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值