并发编程(四)

发布时间:2024年01月12日

线程:数组形成的栈,方法的调用,出栈入栈

1、线程和进程的区别

  1. 资源占用:进程是拥有资源的基本单位,不同进程之间不可以共享资源。线程不占有资源,但一个进程内的所有线程可以共享进程内的资源。
  2. 并发性:进程之间可以并发执行。一个进程内的多个线程也可以并发执行。
  3. 系统开销:创建和销毁进程的时候,系统要分配和回收资源。所以进程的系统开销大于线程。
  4. 执行过程:线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

2、创建线程的方法

  1. 创建一个子类,继承Thread类,重写run方法,在主线程中创建子类对象,然后调用start方法,启动线程。
  2. 创建一个实现类实现Runnable接口,实现run方法,然后再主线程中创建实现类对象,然后把这个对象作为Thread类的参数,创建一个Thread对象,然后调用start方法启动线程。
  3. 创建一个实现类实现Callable接口,实现call方法。 再主线程中创建实现类对象,然后把这个对象作为Thread的参数,创建一个Thread对象,调用start方法启动线程。
  4. 利用线程池创建线程。例如可以使用newSingleThreadExecutor、newCachedThreadPool或newScheduledThreadPool等方式创建线程。

Java代码对线程优先级设置不管用

3、wait()和sleep()的区别

都是立刻让出CPU,sleep不会释放锁锁,wait会释放锁,wait需要事先持有锁

4、如何查看线程信息

在Java中,可以使用以下方法来查看线程信息:

使用Thread类的静态方法currentThread()来获取当前执行的线程对象。然后,可以使用该线程对象的实例方法getId()来获取线程的唯一标识符,getName()来获取线程的名称。

Thread currentThread = Thread.currentThread();

long threadId = currentThread.getId();

String threadName = currentThread.getName();

使用jps命令来查看Java进程的线程信息。打开命令行,进入到jdk的bin目录下,执行jps命令,可以看到当前Java线程的进程ID和名字。然后,使用jstack命令结合进程ID来打印出线程的信息。

jps

jstack <process id>

使用JConsole工具。在jdk的bin目录下有一个JConsole工具,打开它并连接到Java进程,可以在图形界面中查看线程信息。

使用VisualVM工具。VisualVM是一个功能强大的Java性能分析工具,可以查看Java进程的线程信息,包括线程的堆栈跟踪、CPU使用情况等。

使用Java的并发包(java.util.concurrent)。Java提供了一些并发工具类和接口,如ExecutorService、Future、CountDownLatch等,可以帮助开发者更好地管理和控制线程。

5、如何控制线程的执行顺序:

1、通过join控制

2、通过wait notify

3、通过共享变量

6、ThreadLocal:在线程内只能看到自己的变量,看不到其它线程的

ThreadLocal?是 Java 中一个非常有用的类,它提供了线程局部变量。这些变量与普通的变量的区别在于,每个线程都拥有该变量的独立实例,互不干扰。这样,每个线程都可以修改自己的变量副本,而不会影响其他线程的副本。

使用?ThreadLocal?可以解决多线程中的数据同步问题,每个线程都拥有自己的数据副本,避免了对共享数据的并发访问冲突。

实现原理:每个线程调用都会拷贝一份对其进行操作;虚引用

7、CountDownLatch 控制多个线程同时开始

CountDownLatch 是 Java 并发库中的一个类,它允许一个或多个线程等待其他线程完成操作。它提供了一种简单的方法来同步线程的执行。

CountDownLatch 的工作原理是,它维护一个计数器(count),初始化为一个特定的值。每个线程在完成自己的工作后,会调用 countDown() 方法来递减计数器。当计数器递减到 0 时,所有在 await() 方法上等待的线程都会被唤醒并继续执行。

8、线程池:管理线程

它预先创建了若干数量的线程,并且不能由用户直接对线程的创建进行控制,在这个前提下重复使用固定或较为固定数目的线程来完成任务的执行。这样做的好处是,一方面,消除了频繁创建和消亡线程的系统资源开销,另一方面,面对过量任务的提交能够平缓的劣化。

public interface ThreadPool<Job extends Runnable> {
// 执行一个Job,这个Job需要实现Runnable
    void execute(Job job);
// 关闭线程池
    void shutdown();
// 增加工作者线程
    void addWorkers(int num);
// 减少工作者线程
    void removeWorker(int num);
// 得到正在等待执行的任务数量
    int getJobSize();
}

9、java中线程池的几种提交方式

  1. execute()方法:该方法用于提交一个Runnable任务。当线程池中的线程数量小于corePoolSize时,会直接创建一个新的线程来执行任务;当线程数量大于等于corePoolSize时,会将任务放入队列中等待执行。无返回值
  2. submit()方法:该方法用于提交一个Runnable任务或者一个返回Future的对象。它与execute()方法的主要区别在于,submit()方法返回一个Future对象,可以通过该对象获取任务的执行结果或者取消任务。当线程池中的线程数量小于corePoolSize时,会直接创建一个新的线程来执行任务;当线程数量大于等于corePoolSize时,会将任务放入队列中等待执行。

有任务就会先交给核心线程数处理,核心线程数满了就会将线程放入队列中,加快线程的消耗

开启最大线程数的情况下,队列还是满的,就会采用拒绝策略

没有任务的核心线程也会处于运行状态;核心线程执行完成后处于空闲状态,没有满的话,后面的线程会新建一个执行,这样能提高运行速度

在finally块中释放锁,目的是保证在获取到锁之后,最终能够被释放;锁资源如果不被释放,会导致死锁的问题,解决办法:

Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
    lock.unlock();
}

java中finally的作用

无论是否发生异常,都必须要执行的代码块。finally代码块通常用于资源的清理工作,比如关闭文件、释放网络连接等。

文章来源:https://blog.csdn.net/weixin_47480200/article/details/135552812
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。