关于线程的一些知识点

程序:是为了完成特定任务,用某种语言编写的一组指令的集合,简单来说就是,我们写的代码

进程:是程序的一次执行过程,或是正在运行的一个程序,是动态过程,有它自身的产生,存在和消亡的过程

Runtime类是Java中与运行时环境相关的类,主要用于获取当前JVM的运行状态、内存使用情况、执行系统命令等操作。其中,availableProcessors()方法可以获取当前计算机的CPU核心数量,该方法返回一个int类型的值。           这段代码使用Java中的Runtime类获取当前运行时环境,然后调用其getRuntime()方法获取Runtime实例。接着通过调用availableProcessors()方法获取当前电脑的CPU核心数量,并将其赋值给变量cpuNum。最后通过调用System.out.println()方法将cpuNum输出到控制台

方法一

继承Thread类 继承Thread类:通过继承Thread类,重写其`run()`方法,然后创建线程对象并调用`start()`方法来启动线程。

class MyThread extends Thread {
    public void run() {
        // 线程执行的代码
    }
}

// 创建线程对象并启动线程
MyThread thread = new MyThread();
thread.start();

优点::编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this即可获取当前线程

缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以就不能再继承其他父类了。

方法二

实现Runnable接口:通过实现Runnable接口,实现其`run()`方法,然后创建Thread对象并将Runnable对象作为参数传递给Thread的构造函数。

class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
    }
}

// 创建Runnable对象
Runnable myRunnable = new MyRunnable();

// 创建Thread对象,并将Runnable对象传递给Thread的构造函数
Thread thread = new Thread(myRunnable);

// 启动线程
thread.start()

方法三

实现Callable接口( JDK1.5>= )  使用Callable和Future:通过实现Callable接口,实现其`call()`方法,然后使用ExecutorService来提交Callable任务,并通过Future对象来获取返回结果

class MyCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        // 线程执行的代码
        return result;
    }}
// 创建Callable对象
Callable<Integer> myCallable = new MyCallable();
// 创建ExecutorService对象
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交Callable任务并获取Future对象
Future<Integer> future = executor.submit(myCallable);
// 获取线程返回结果
Integer result = future.get();
// 关闭ExecutorService
executor.shutdown();

优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程 可以共享一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将 CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。 缺点:编程稍微复杂一些,如果需要访问当前线程,则必须使用 Thread.currentThread() 方法

思考:为什么Runnable不能直接调用start方法,必须作为参数传递给thread对象

这是因为,Java中是通过Thread类来实现线程的,Runnable接口只是描述了线程要执行的任务,而没有实现线程的具体功能。当我们创建一个线程时,需要将Runnable对象作为线程的任务,然后将该任务交给Thread类来执行。因此,必须将实现了Runnable接口的对象作为参数传递给Thread类的构造方法,从而将该对象与Thread类关联起来,才能启动线程。 如果我们直接调用实现了Runnable接口的对象的start()方法,实际上是在该对象上调用了Thread类中的start()方法,而此时并没有将该对象与Thread类关联起来,因此无法启动线程

线程池方式创建

// 创建线程池对象
ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交Runnable任务给线程池执行
executor.execute(new Runnable() {
    public void run() {
        // 线程执行的代码
    }
});

// 关闭线程池
executor.shutdown();

Executors 是一个工具类,提供了创建不同类型线程池的静态方法。 newFixedThreadPool(5) 方法返回一个固定大小的线程池,其中可以同时执行 5 个线程任务。 ExecutorService 是一个接口,用于管理和控制线程池中的线程。它继承了 Executor 接口,并在其基础上提供了更多的功能,比如提交任务、关闭线程池等。 通过将 newFixedThreadPool(5) 返回的线程池对象赋值给 ExecutorService 类型的变量 executor,我们可以使用该变量来操作线程池。

线程池创建线程的优点: 提高系统性能:线程池可以复用线程,减少线程的创建和销毁开销,提高系统的性能。 资源管理:线程池可以控制线程的数量,避免线程数量过多导致系统资源耗尽。 线程池创建线程的缺点: 可能会浪费资源:如果线程池的大小设置不合理,可能会浪费系统资源或者导致任务等待时间变长。 可能会引起死锁:如果线程池中的任务依赖于其他任务的结果,可能会发生死锁现象。

ThreadLocal

ThreadLocal是Java中的一个线程局部变量,它可以让每个线程在访问同一个变量时,都有自己独立的副本。ThreadLocal通常用于解决多线程并发访问共享变量的线程安全问题。 ThreadLocal的基本用法是,首先创建一个ThreadLocal对象,然后通过调用它的set()方法来设置当前线程的局部变量值,通过调用get()方法来获取当前线程的局部变量值。

public class UserContext {
    private static final ThreadLocal<String> userThreadLocal = new ThreadLocal<>();

    public static void setUser(String username) {
        userThreadLocal.set(username);
    }

    public static String getUser() {
        return userThreadLocal.get();
    }
}

使用ThreadLocal的好处在于,每个线程都有自己独立的副本,它们之间互不干扰,不需要加锁或者使用同步机制,从而避免了线程安全问题。例如,在上述代码中,如果多个线程同时访问UserContext.setUser()方法,每个线程都会有自己独立的副本,不会相互干扰,从而保证了线程安全。 需要注意的是,ThreadLocal虽然可以解决线程安全问题,但过多的使用ThreadLocal也会导致内存泄漏和性能问题,因为每个ThreadLocal都会占用一定的内存,而且ThreadLocal的使用也会增加线程上下文切换的开销。因此,在使用ThreadLocal时需要注意控制数量和生命周期,避免出现内存泄漏和性能问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值