线程基础

250 阅读5分钟

第一次写技术博客,有不对的地方,大家多多指教,闲话不多说请看下文

一、线程的概念

说到线程肯定会扯到进程,进程是什么?线程又是什么?大部分人都知道,进程通俗的来讲就是正在执行的应用程序,大家可以打开自己的任务管理器,可以看到好多正在执行的程序,这就是进程,而在进程中又包含很多线程,它是进程的组成部分

二、JAVA线程的分类

java中线程分为两种类型:用户线程和守护线程。 通过Thread.setDaemon(false)设置为用户线程; 通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程。 守护线程为用户线程服务,当用户线程全部退出,则守护线程就停止了,GC就是守护线程

三、线程的创建方式

1、继承Thread类 代码一 public class CreateThread extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(i); } } public static void main(String[] args) { CreateThread crThread=new CreateThread(); System.out.println("创建线程开始"); crThread.start(); System.out.println("创建线程结束"); }

} 2、实现Runable接口 代码二 public class CreateRunable implements Runnable{

@Override
public synchronized void run() {
    for(int i=0;i<10;i++){
        System.out.println(i);
    }
}
public static void main(String[] args) {
 CreateRunable crThread=new CreateRunable();
    System.out.println("创建线程开始");
    Thread read=new Thread(crThread);
    Thread read1=new Thread(crThread);
    read.run();
    read1.run();
    System.out.println("创建线程结束");
    
}

} 3、匿名内部类创建线程 代码三 public static void main(String[] args) { //匿名内部类 Thread noName=new Thread(new Runnable() {

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(i+"=");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
        }
    });
    noName.run();
}

3、使用Callable和Future创建线程 public class ThreadTest {

public static void main(String[] args) {

    Callable<Integer> myCallable = new MyCallable();    // 创建MyCallable对象
    FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象

    for (int i = 0; i < 100; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
        if (i == 30) {
            Thread thread = new Thread(ft);   //FutureTask对象作为Thread对象的target创建新的线程
            thread.start();                      //线程进入到就绪状态
        }
    }

    System.out.println("主线程for循环执行完毕..");
    
    try {
        int sum = ft.get();            //取得新创建的新线程中的call()方法返回的结果
        System.out.println("sum = " + sum);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

}

}

class MyCallable implements Callable { private int i = 0;

// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
    int sum = 0;
    for (; i < 100; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
        sum += i;
    }
    return sum;
}

} 4、使用线程池 后面会单独抽出一篇文章讲这个

四、线程的生命周期

线程声明周期一般分为以下五种 1、新建(new Thread) 当创建线程的一个实例,该线程处于新建状态 2、就绪(runable) 当线程被启动等待CPU分配时间片,也就是说线程正在就绪队列中等待获得CPU资源 例如集成Thread和实现runable接口的线程实例调用start()方法使线程处于就绪状态 3、运行(running) 线程获得CPU分配的资源后开始运行(执行run方法),此时线程除非自动放弃CPU资源或者有更高的优先级别进入,线程将运行结束 4、阻塞 由于某种原因导致正在运行的线程让出CPU资源停止自己的执行,即线程进入阻塞状态 一般遇到下面情况线程会进入阻塞状态  堵塞的情况分三种:  (一)等待堵塞:执行的线程执行wait()方法,JVM会把该线程放入等待池中。  (二)同步堵塞:执行的线程在获取对象的同步锁时,若该同步锁被别的线程占用。则JVM会把该线程放入锁池中。  (三)其它堵塞:执行的线程执行sleep()或join()方法,或者发出了I/O请求时。JVM会把该线程置为堵塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完成时。线程又一次转入就绪状态。  几个常用的阻塞方法解释 ①线程调用sleep()方法使线程处于睡眠状态,睡眠时间到了会自动进入运行状态 ②调用wait()方法使线程处于等待状态此时可以被notify()唤醒 ③yield方法:放弃当前时间片,将Running状态转变为Runnable状态,不能指定多长时间。//假装忍让,完成具有不确定性不受监督的切换 ④join方法:类似sleep,停止当前线程,让join线程先执行完毕,或执行指定的时间。//插队拼接 5、死亡 当线程执行完毕或者被其他线程杀死就会进入死亡状态,此时该线程不会在进入就绪队列进行等待执行 自然终止:正常运行run()方法后终止 异常终止:调用stop()方法让一个线程终止运行

五、线程生命周期转换图

1、整体图

2、详细的过程转换图

五、线程基础常问的面试题

  1. 进程与线程的区别? 答:进程是所有线程的集合,每一个线程是进程中的一条执行路径,线程只是一条执行路径。
  2. 为什么要用多线程? 答:提高程序效率
  3. 多线程创建方式? 答:继承Thread或Runnable 接口。
  4. 是继承Thread类好还是实现Runnable接口好? 答:Runnable接口好,因为实现了接口还可以继续继承。继承Thread类不能再继承。
  5. 你在哪里用到了多线程? 答:主要能体现到多线程提高程序效率。
  6. 举例:分批发送短信、迅雷多线程下载等。