扫盲篇之 ~线程的创建方式、线程的六个状态、何为线程死锁

一 线程三种基本创建方式

1.1 继承Thread

public class Demo1 {
    public static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("创建线程");
        }
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //开启线程
        myThread.start();
        
    }
}

线程调用run( ) 和start( )的区别?
不能用run( )来开启新线程,它只会在当前线程中串行的执行run( )方法。默认情况下thread的run方法什么都没做,需要进行重载。

1.2 实现runnable创建线程

public static class RunableTask implements Runnable{
        @Override
        public void run() {
            System.out.println("实现runnable创建线程");
        }
    }
    public static void main(String[] args) {
        RunableTask runableTask = new RunableTask();
        new Thread(runableTask).start();
    }

上面介绍的两种方式都有一个缺点,就是任务没有返回值。下面看最后一种,即使用FutureTask 的方式。

1.3 实现callable创建线程

 public static class CallerTask implements Callable<String>{
        @Override
        public String call() throws Exception {
            return "callable创建线程";
        }
    }
    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask  = new FutureTask<String>(new CallerTask());
        /*String s1 = futureTask.get();*/
        /*System.out.println("first"+ s1);*/
        new Thread(futureTask).start();
        String s = futureTask.get();
        System.out.println(s);
    }

二、线程的六个状态

在这里插入图片描述

2.1 六个状态:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回 。
  6. 终止(TERMINATED):表示该线程已经执行完毕。

2.2 图示方法的解读:

• Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
• Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
• t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。
• obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。(只能写在同步代码块中)
• obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。(只能写在同步代码块中)。

三、理解线程上下文切换

在多线程编程中,线程个数一般都大于CPU个数,而每个CPU同一时刻只能被一个线程使用,为了让用户感觉多个线程是在同时执行的,CPU资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用CPU 执行任务。当前线程使用完时间片后,就会处于就绪状态并让出CPU 让其他线程占用,这就是上下文切换,
从当前线程的上下文切换到了其他线程。那么就有一个问题,让出CPU的线程等下次轮到自己占有CPU时如何知道自己之前运行到哪里了?所以在切换线程上下文时需要保存当前线程的执行现场,当再次执行时根据保存的执行现场信息恢复执行现场。
线程上下文的切换可能会导致程序越来越慢。

四、线程死锁

4.1 什么是线程死锁?

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去。如下图所示。
那么为什么会产生死锁呢? 学过操作系统的朋友应该都知道,死锁的产生必须具备以下四个条件。
• 互斥条件:指线程对己经获取到的资源进行排它性使用, 即该资源同时只由一个线程占用。如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。
• 请求并持有条件:指一个线程己经持有了至少一个资源,但又提出了新的资源请求,而新资源己被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己己经获取的资源。
• 不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。
• 环路等待条件:指在发生死锁时, 必然存在一个线程→资源的环形链, 即线程集合{TO , TL T2 ,…,Tn }中的TO 正在等待一个Tl 占用的资源, Tl 正在等待T2 占用的资源,……Tn 正在等待己被TO 占用的资源。

五、守护线程与用户线程

5.1 介绍

Java 中的线程分为两类,分别为daemon 线程(守护线程〉和user 线程(用户线程)。在JVM启动时会调用main 函数,main 函数所在的钱程就是一个用户线程,其实在JVM内部同时还启动了好多守护线程,比如垃圾回收线程。那么守护线程和用户线程有什么区别呢?
区别之一是当最后一个非守护线程结束时,JVM会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出。言外之意,只要有一个用户线程还没结束, 正常情况下JVM就不会退出。

5.2 创建守护线程代码示例

package com.hll.demo;
/**
 * @author wb-hll364276
 * @date 2020/4/29.
 */
public class Demo6 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("1234");
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值