快速入门多线程(初阶一)

1.认识线程

  • 1.1 什么是线程
    百度百科:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程

我们可以将阿里巴巴看做一个进程,那么线程就是马云(马云干自己该干的事)
1.2 多线程的优势?
相比于单线程,多线程的效率更高(众人拾柴火焰高)

2.线程的实现

2.1 线程的实现有三种方式

  1. 继承Thread,重写run方法
  2. 实现Runnable接口,重写run方法
  3. 实现callable接口,

创建线程 ---------继承Thread


public class ThreadDemo extends Thread {
    @Override
    public void run() {
        //TODO 实现具体的逻辑
    }
}

创建线程--------实现Runnable 接口, 再实现run()

public class ThreadDemo implements Runnable{

    public void run() {
        //TODO 实现具体的逻辑
    }
}

创建线程--------实现callable接口

public class ThreadDemo implements Callable {

    public Object call() throws Exception {
        //TODO 实现具体的逻辑
        return null;
    }
}

2.2 Thread 的构造方法

方法说明
public Thread()分配一个新的Thread对象
public Thread(Runnable target)使用 Runnable 对象创建线程对象
public Thread(String name)name为新线程的名字
public Thread(Runnable target, String name)使用 Runnable 对象创建线程对象 ,并命名

2.3线程的常用属性

属性方法说明
IDgetId()线程ID是唯一的,并且在其生命周期内保持不变。 当线程被终止时,该线程ID可以被重用。
名称getName()返回此线程的名称。
状态getState()返回此线程的状态。 该方法设计用于监视系统状态,不用于同步控制。
优先级getPriority() setPriority ()获取线程的优先级和设置线程的优先级,其中MIN_PRIORITY =1 ,MAX_PRIORITY=10,NORM_PRIORITY=5,如果不设置默认为5
是否为后台线程isDaemon()测试这个线程是否是守护线程。
是否存活isAlive()测试这个线程是否活着。 如果一个线程已经启动并且尚未死亡,那么线程是活着的。
是否中断interrupted()该方法可以清除线程的中断状态 。 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false(除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经检查过)。
  • 什么是守护线程呢?
    1.守护线程(例如垃圾回收线程:gc线程)
    2.非守护线程(用户线程:用户线程即我们手动创建的线程)
  • 两者的区别
    1.守护线程有一个特征,例如当主线程运行的时候,垃圾回收线程一起运行。当主线程销毁,会和主线程一起销毁。
    2.非守护线程
    如果主线程销毁,用户线程继续运行且互不影响。

2.4 启动一个线程-----start()
创建一个线程时,我们要重写其中的run(),但线程对象被创建出来并不代表者该线程已经启动,run()中我们想让该线程做的事情,要想让该线程启动,我们还需手动的调用start()方法。
这里就有一个面试题了:

面试题:start()与run()的区别:
系统调用线程类的start()方法来启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM来调度执行。在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作,当run()方法结束后,此线程就会终止。
如果直接调用线程类的run()方法,这会被当做一个普通的函数调用,程序中仍然只有主线程这一个线程,也就是说,start()方法能够异步地调用run()方法,但是直接调用run()方法却是同步的,因此也就无法达到多线程的目的。
​ 只有通过调用线程类的start()方法才能真正达到多线程的目的。

2.5 中断一个线程-----interrupt()

interrupt()方法他是一个"通信员",什么意思呢?当一个线程调用这个方法,这个线程会不会中断由线程内部的代码决定,而不是调用interrupt后这个线程就中止了,只是通知该线程停止运行。

在每一个线程内部都有一个标志位,默认为false,当调用这个方法后会将标志位改为true,可以通过Thread.isInterrupted()或Thread.currentThread().isInterrupted() 来判断标志位是否被改。
这两个方法也是有区别的,Thread.isInterrupted()此方法调用后会清除标志位,即又将标志位变为false
Thread.currentThread().isInterrupted() 不会清除标志位

当线程调用了wait()/sleep()/join()阻塞挂起时候,以 InterruptedException 异常的形式通知,并清除中断标志

测试代码:

public class Test {
    public static void main(String[] args) throws InterruptedException {
       Thread t = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   while (true) {
                       for(int i = 0; i < 1000; i++ ){
                           System.out.println("正在打印....");
                       }
                       Thread.sleep(200);
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   System.out.println(Thread.currentThread().isInterrupted());
               }
           }
       });
       t.start();
       Thread.sleep(1);
       t.interrupt();
    }
}

在这里插入图片描述
通过打印结果我们可以看到,在抛出异常前标志位变成了false;

public class Test {
    public static void main(String[] args) throws InterruptedException {
       Thread t = new Thread(new Runnable() {
           @Override
           public void run() {
               while (!Thread.interrupted()) {
                   for(int i = 0 ; i < 1000 && !Thread.interrupted(); i++){
                       System.out.println("正在打印...."+i);
                   }
               }
           }
       });
       t.start();
       Thread.sleep(1);
       t.interrupt();
    }
}

在这里插入图片描述
死循环一直在打印,Thread.interrupt()清除了标志位。
2.6等待一个线程

public class Test {
    public static void main(String[] args) throws InterruptedException {
       Thread t = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1000);
                   System.out.println("t线程结束...");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });
       t.start();
       System.out.println("主线程结束...");
    }
}

当我们执行上述代码时候,我们会发现运行结果是: 在这里插入图片描述
有时候我们需要让主线程最后结束,这个时候我们就要用到了join(),join方法时使当前线程放弃抢夺cpu,直到调用的线程结束,什么意思呢?我们来看接下来的代码

Thread t = new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1000);
                   System.out.println("t线程结束...");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });
       t.start();
       t.join();
       System.out.println("主线程结束...");

在这里插入图片描述

在主线程内,使用了t.join()就会是主线程进入阻塞挂起状态,直到t线程结束。也就是说在A线程里调用了B线程的join方法,A线程就会阻塞挂起,直到B线程结束。
2.7休眠当前线程

方法解释
public static void sleep(long millis)休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos)可以更高精度的休眠

sleep()方法是使当前线程睡眠,不在去抢夺cup,直到睡眠结束

3.线程的状态

上面我们就提到了阻塞挂起,这只是线程的一个状态,现在我们来看一看线程都有哪些状态

public static void main(String[] args) throws InterruptedException {
      for(Thread.State state :Thread.State.values()){
          System.out.println(state);
      }
    }

通过该代码我们可以看到,在java中线程共有 6中状态
在这里插入图片描述
1.NEW : 指的是线程被创建但没有启动,即没有调用strat();
2.RUNNABLE : 与操作系统中不同的是Java中的RUNNABLE状态不一定是在运行态,还可能是在等待cpu,这个状态就是指该线程已经拥有了抢夺cpu的资格,但不一定已经抢到了,他包含了(RUNNING,READY)两种状态
3.BLOCKED:等待监视锁,这个时候线程被操作系统挂起。当进入synchronized块/方法或者在调用wait()被唤醒/超时之后重新进入synchronized块/方法,锁被其它线程占有,这个时候被操作系统挂起,状态为阻塞状态
4.WAITING:(无限期的等待,不知道什么时候结束)使用 join(),wait()方法等方法后进入此状态,已经没有抢夺cup的资格。
5.TIMED_WAITING:当线程调用sleep(睡眠时间)/wait(等待时间)/join(等待时间)/ LockSupport.parkNanos(等待时间)/LockSupport.parkUntil(等待时间)方法之后所处的状态,在指定的时间没有被唤醒或者等待线程没有结束,会被系统自动唤醒,正常退出。
6.TERMINATED:线程终止。
线程终止的方式:

  1. 正常结束,run()方法运行完成
  2. stop()已启用,太过暴力,可能会丢失数据
  3. interrupt() ,通知线程 然后在线程内部结束线程

观察 1: WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换

public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        Thread t = new Thread(() -> {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 1000_0000; i++) {}
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println(t.getState());;
        t.start();
        System.out.println(t.getState());
        Thread.sleep(10);
        synchronized (object) {
            for (int i = 0; i < 10; i++) {
                System.out.println(t.getState());
            }
            object.notify();
        }
        while (t.isAlive()) {
            System.out.println(t.getState());
        }
    }

线程状态的转移图
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值