Thread

本文详细介绍了Java中线程阻塞机制的概念及其应用场景,包括sleep(), suspend(), yield(), wait()和notify()等方法的功能及使用场景。此外,还对比了synchronized与volatile的区别,以及ThreadLocal的作用。

阻塞机制

阻塞机制的本质是为了解决线程通信的同步问题。锁和阻塞机制解决线程通信中的互斥和同步问题。

为了解决对共享存储区的访问冲突,引入了锁机制,考察多个线程对共享资源的访问,显然锁机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,引入了对阻塞机制的支持。

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

  1. sleep()方法:sleep()允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
  2. suspend()和resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用resume()使其恢复。
  1. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。
  2. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

关于 wait() 和 notify() 方法

  1. 调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
  2. 除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

synchronized和volatile的区别:

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是
    立即可见的。
2)禁止进行指令重排序。
   volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
   synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。


1.volatile仅能使用在变量级别;
   synchronized则可以使用在变量、方法、和类级别的

2.volatile仅能实现变量的修改可见性,并不能保证原子性;

   synchronized则可以保证变量的修改可见性和原子性

3.volatile不会造成线程的阻塞;
   synchronized可能会造成线程的阻塞。

4.volatile标记的变量不会被编译器优化;
   synchronized标记的变量可以被编译器优化



线程优先级

线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。

你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。  



ThreadLocal:

              java.lang.ThreadLocal是local  variable(线程局部变量)他为每一个使用该变量的线程都提供了一个变量值的副本,使每个线程都可以独立的改变自己的副本,而不会和其他线程的副本冲突。从线程的角度来看,就好像每个线程都拥有该变量。ThreadLocal本质是一个线程安全的hashMap,key为threadName,value为线程内的变量。

            Synchronized用于线程间的数据共享,而ThreadLocal则用于数据间的数据隔离。

            ThreadLocal主要解决多线程中数据并发而产生不一致的问题。ThreadLocal通过访问数据对象的副本来运行业务,这样的结果是耗费了内存,但是大大减少了线程同步所带来的性能消耗,也减少了线程并发控制的复杂度。

            ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。




Thread 类是 JVM 用于管理线程的类,每个线程都有一个 Thread 对象与之关联,JVM 会将这些 Thread 对象组织起来,用于线程调度和管理[^2]。 ### 功能与特性 Thread 对象具有 ID、名称、优先级、状态等属性,这些属性被 JVM 用于线程的调度和管理。在多线程环境下,根据多线程的内存原理,会开辟包含主线程的多个栈空间供 CPU 执行不同线程,不过线程执行的优先级通常由 CPU 决定,用户较难控制[^2][^3]。 ### 常见构造方法 - `Thread()`:创建一个线程对象。 - `Thread(Runnable target)`:使用 Runnable 对象创建线程对象。 - `Thread(String name)`:创建线程对象,并为其命名。 - `Thread(Runnable target, String name)`:使用 Runnable 对象创建线程对象,并为其命名[^2]。 ### 使用方法 #### 线程创建 - **继承 Thread 类,重写 run 方法** ```java class MyThread extends Thread { @Override public void run() { System.out.println("继承 Thread 类创建的线程在执行"); } } // 使用示例 MyThread thread = new MyThread(); thread.start(); ``` - **实现 Runnable 接口,重写 run 方法** ```java class MyRunnable implements Runnable { @Override public void run() { System.out.println("实现 Runnable 接口创建的线程在执行"); } } // 使用示例 Runnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); ``` - **继承 Thread 类,重写 run 方法,使用匿名内部类** ```java Thread thread = new Thread() { @Override public void run() { System.out.println("使用匿名内部类继承 Thread 类创建的线程在执行"); } }; thread.start(); ``` - **实现 Runnable 接口,重写 run 方法,使用匿名内部类** ```java Runnable runnable = new Runnable() { @Override public void run() { System.out.println("使用匿名内部类实现 Runnable 接口创建的线程在执行"); } }; Thread thread = new Thread(runnable); thread.start(); ``` - **使用 lambda 表达式(最推荐)** ```java Thread thread = new Thread(() -> { System.out.println("使用 lambda 表达式创建的线程在执行"); }); thread.start(); ``` #### 线程中断 - **使用标志位来控制线程是否要停止** ```java class MyThread extends Thread { private volatile boolean isStopped = false; @Override public void run() { while (!isStopped) { System.out.println("线程正在执行"); } System.out.println("线程已停止"); } public void stopThread() { isStopped = true; } } // 使用示例 MyThread thread = new MyThread(); thread.start(); thread.stopThread(); ``` - **使用 Thread 自带的标志位来控制线程是否要停止** ```java Thread thread = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { System.out.println("线程正在执行"); } System.out.println("线程已停止"); }); thread.start(); thread.interrupt(); ``` #### 线程等待 在 Java 中,可以使用 `join()` 方法让一个线程等待另一个线程执行完毕。例如: ```java Thread thread1 = new Thread(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程 1 执行完毕"); }); Thread thread2 = new Thread(() -> { try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程 2 在等待线程 1 执行完毕后执行"); }); thread1.start(); thread2.start(); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值