Java线程梳理

目录

一,线程的概念

二,线程的特点

三,线程分类

四,多线程涉及到的概念

并发

并行

 多线程程序的优点 

五,线程的创建

Thread对象的创建(线程的创建)

 第一种实现方式-继承Thread

第二种实现方式-实现Runnable接口

 第三种实现方式-实现Callable接口

第四种实现方式-实现使用线程池

六,线程常用方法

构造方法

其他方法

线程的生命周期


一,线程的概念

线程包含在进程中,线程是进程的一条执行路径。

在进程内部,可以同时运行多个“子任务”,也就是多个线程。在一个进程中,多个线程共享内存。线程是进程的最小单位。

二,线程的特点

线程与线程之间是相互独立的。

三,线程分类

单线程:当进程中仅包含一个执行指令的线程时,这样的进程成为单线程(也叫主线程)。

多线程:一个进程拥有多个线程,线程之间相互协作,共同执行一个应用程序。

四,多线程涉及到的概念

并发

从生活中看:

并发指在一段时间宏观上同时去处理多个任务。

从CPU看:

一段时间内,有多条指令在单个CPU上快速轮流交替执行,使得在宏观上具有多个进程同时执行的效果。

并发概念:

在一段时间内宏观上同时(交替执行的)去处理多个任务。

并行

从生活中看:

同一个时刻,多个任务确实真的同时运行。 -在同一时刻,多个人同时的做多件事情

从CPU看:

指在同一时刻,有多条指令在多个CPU上同时执行。比如:多个人同时做不同的事。

并行概念:

并行同一个时刻,多个任务确实真的同时运行。 -在同一时刻,多个人同时的做事情

 多线程程序的优点 

提高应用程序的响应。对图形化界面更有意义,可增强用户体验。

提高计算机系统CPU的利用率

改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

五,线程的创建

Thread对象的创建(线程的创建)
  • 在Java中创建线程就是创建Thread对象,常用构造方法有:
  1. Thread()
  2. Thread(Runnable target)  
 第一种实现方式-继承Thread

Java通过继承Thread类来创建并启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程
public class SubThread extends Thread {
    String threadName;
    public SubThread(String threadName){
        this.threadName = threadName;
    }
    //2) 重写run()方法
    //run()方法体就是子线程要执行的代码, 线程是程序的一个执行单元,线程需要完成某个任务, 把线程完成任务的代码放在run()方法体中
    @Override
    public void run() {
        //打印100行字符串
        for (int i = 0; i < 100; i++) {
            System.out.println( this.threadName+"------> " + i );
        }
    }
}

public class Test01 {
    public static void main(String[] args) {
        //3) 创建线程对象
        //Thread  t = new SubThread();//Thread父类引用赋值子类对象,对象自动向上转型
        SubThread t = new SubThread("线程t1");//new出来一个SubThread对象,调用SubThread()构造方法,在子类构造方法体执行前,系统会先执行父类构造方法,默认情况下调用父类的无参构造Thread()

        //4)开启线程
        t.start();//start()方法仅仅是告诉操作系统的线程调度器,当前t线程准备就绪, 等线程调度器选择执行t线程时, 会为t线程分配线程栈, 新分配 的线程就会自动执行run()方法
        //t.run();//注意直接调用run()就是普通实例方法的调用, 不会开启新的线程

        //创建第二个线程
        SubThread t2 = new SubThread("线程t2");
        //开启线程
        t2.start();
        /*
            1) 运行程序, 两个线程在并发执行
            2) 注意线程执行结果是随机的,每次运行结果可能都不一样
            3) start()调用的先后顺序并不一定就是线程开启的先后顺序
            4) 注意一个线程只能 start()一次
            5) 如果同学们每次运行结果都一样,适当的把循环次数增加到10000次
         */
        //t.start();//IllegalThreadStateException
    }
}
第二种实现方式-实现Runnable接口
public class Prime implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println( " Prime -----------------> " + i );
        }
    }
}

public class Test01 {
    public static void main(String[] args) {
        Prime prime = new Prime();
        Thread t1 = new Thread(prime);
        t1.start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(" annonymous ----> " + i);
                }
            }
        }).start();
    }
}

继承Thread:线程代码存放Thread子类run方法中。

实现Runnable:线程代码存在接口的子类的run方法。

实现Runnable接口比继承Thread类所具有的优势:

1.避免了单继承的局限性

2.多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

 第三种实现方式-实现Callable接口

特点:可以获得到多线程的结果/

1,创建一个MyCallable实现Callable接口。

2,重写call。(有返回值,表示多线程运行的结果)。

3,创建的对象(表示多线程要执行的任务)。

4,创建FutureTask的对象(左右管理多线程运行结果)。

5,创建Thread类的对象,并启动(表示线程)。

public class NumThread implements Callable {

    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0;i <= 100;i++){
            if (i % 2 ==0){
                sum += i;
            }
        }

        return sum;
    }
}

public class CallableTest {
    public static void main(String[] args) {
        NumThread numThread = new NumThread();
        FutureTask futureTask = new FutureTask<>(numThread);
        /*调用Thread( Runnable ) 构造方法创建Thread对象, 借助FutureTask类FutureTask类
        实现了RunnableFuture接口, 而RunnableFuture接口继承了Runnable接口,
        所以 FutureTask类是Runnable接口的实现类,  在调用Thread(Runnable)
        构造方法时, 实参可以传递FutureTask对象调用  FutureTask(Callable)
        构造方法创建对象, 形参是Callable接口,实参传递接口实现类对象. 通过
        泛型指定任务返回值类型为Integer
        */
        Thread t1 = new Thread(futureTask);
        t1.start();

        //      接收返回值
        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
第四种实现方式-实现使用线程池

问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

 解决思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

 好处:

  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理
  • corePoolSize:核心池的大小
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程没有任务时最多保持多长时间后会终止
class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread2 implements Callable {
    @Override
    public Object call() throws Exception {
        int evenSum = 0;//记录偶数的和
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                evenSum += i;
            }
        }
        return evenSum;
    }
}

public class ThreadPoolTest {
    public static void main(String[] args) {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//        //设置线程池的属性
//        System.out.println(service.getClass());//ThreadPoolExecutor
        service1.setMaximumPoolSize(50); //设置线程池中线程数的上限

        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

        try {
            Future future = service.submit(new NumberThread2());//适合使用于Callable
            System.out.println("总和为:" + future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //3.关闭连接池
        service.shutdown();
    }
}

六,线程常用方法

构造方法

public Thread():分配一个新的线程对象。

public Thread(String name):分配一个指定名字的新的线程对象。

public Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法.

public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

其他方法

public void run() :此线程要执行的任务在此处定义代码。

public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。

public String getName() :获取当前线程名称

public void setName(String name):设置该线程名称。

public static Thread currentThread() :返回对当前正在执行的线程对象的引用。在Thread子类中就是this,通常用于主线程和Runnable实现类

public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

void join() :等待该线程终止。

void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。

void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

线程的生命周期

在java.lang.Thread.State的枚举类中这样定义:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

NEW新建,RUNNABLE可执行,Teminated被终止,死亡(BLOCKED锁阻塞,TIMED_WAITING计时等待,WAITING无限等待)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值