Java线程阻塞

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一
定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

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

  上述的核心区别导致了一系列的细节上的区别。

  首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,
所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞
时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象
上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随
机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

  其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块
中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的
道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调
用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若
不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。

  wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和
操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作
系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语
(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间
通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

  关于 wait() 和 notify() 方法最后再说明两点:
  第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随
机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问
题。

  第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用
notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有
获得锁的那一个线程才能进入可执行状态。
  谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait()
方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小
心地避免死锁。

  以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify()
方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用
中我们应该灵活使用各种方法,以便更好地达到我们的目的。
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Java 中,可以使用以下方式实现线程阻塞: 1. 通过调用 `Thread.sleep()` 方法来使线程暂停一段时间。该方法会使当前线程暂停执行一段指定的时间,但不会释放线程所持有的锁。 ```java try { Thread.sleep(1000); // 线程暂停1秒钟 } catch (InterruptedException e) { e.printStackTrace(); } ``` 2. 使用 `wait()` 方法来使线程进入等待状态。当一个线程执行了 `wait()` 方法后,它会释放掉它所持有的锁,并且进入等待状态,直到被其它线程通过 `notify()` 或 `notifyAll()` 方法唤醒。 ```java synchronized (obj) { try { obj.wait(); // 线程进入等待状态 } catch (InterruptedException e) { e.printStackTrace(); } } ``` 3. 通过 `join()` 方法使一个线程等待另一个线程的执行完成。当一个线程执行了 `join()` 方法后,它会等待指定的线程执行完成后再继续执行。 ```java Thread thread = new Thread(new Runnable() { @Override public void run() { // 线程执行的代码 } }); thread.start(); try { thread.join(); // 等待指定线程执行完成 } catch (InterruptedException e) { e.printStackTrace(); } ``` 需要注意的是,当线程进入等待状态后,它会释放掉它所持有的锁,这是为了避免死锁的情况。因此,在使用 `wait()` 方法时,必须先获得锁对象的监视器锁。 ### 回答2: 在Java中,线程可以使用以下几种方法实现阻塞。 1. 使用`Thread.sleep()`方法:可以在线程中调用`Thread.sleep()`方法来阻塞线程的执行一段时间。该方法接受一个以毫秒为单位的参数,指定线程要休眠的时间。调用`Thread.sleep()`方法后,线程会进入阻塞状态,暂停执行指定的时间后恢复执行。 2. 使用`wait()`和`notify()`方法:线程可以使用`wait()`方法进入阻塞状态,在其他线程调用相同对象的`notify()`或`notifyAll()`方法时被唤醒。`wait()`和`notify()`方法通常与`synchronized`关键字一起使用,以确保线程安全。 3. 使用`Lock`和`Condition`:Java中的`Lock`接口提供了更灵活的线程同步机制,它可以替代`synchronized`关键字来实现线程阻塞和唤醒。通过创建一个`ReentrantLock`对象,可以使用其`newCondition()`方法创建一个`Condition`对象。线程可以使用`Condition`的`await()`方法进入阻塞状态,并使用`signal()`或`signalAll()`方法通知其它线程。 4. 使用`BlockingQueue`:`BlockingQueue`是一个带有阻塞功能的队列,可以用来实现线程阻塞和唤醒。线程可以调用`BlockingQueue`的`put()`方法将元素放入队列中,如果队列满了,线程会被阻塞等待;而调用`take()`方法从队列中取出元素,如果队列为空,线程也会被阻塞等待。 5. 使用`Future`和`Callable`:线程可以使用`Future`和`Callable`接口来实现阻塞。`Callable`是一个带有返回值的任务,而`Future`则是对任务执行结果的引用。通过调用`Future`的`get()`方法,线程可以阻塞等待任务的执行结果。 这些方法都可以在适当的地方使用,以实现Java线程阻塞。具体使用哪种方法,取决于具体的应用场景和需求。 ### 回答3: 在Java中,线程可以通过以下几种方式实现阻塞。 1. Object类中的wait()和notify()方法:可以使用wait()方法使线程进入等待状态,直到其他线程调用相同对象上的notify()或notifyAll()方法才能被唤醒。 2. sleep()方法:通过Thread类的静态方法sleep(),可以使当前线程暂停执行一段时间。在睡眠期间,线程阻塞的,不会占用CPU时间。 3. I/O阻塞:当线程执行输入/输出操作时,如果数据未就绪,线程会自动阻塞。例如,使用阻塞式的Socket读取或写入数据时,如果没有数据可用,线程将被阻塞,直到数据准备好。 4. 同步阻塞:当线程需要获取一个已经被其他线程持有的锁时,它将进入阻塞状态,直到锁被释放。这可以通过使用synchronized关键字来实现。线程可以使用synchronized关键字来获取一个对象上的锁,并在执行同步代码块时阻塞其他线程的访问。 5. 等待线程执行完毕:可以使用Thread类的join()方法,使一个线程等待另一个线程执行完毕后再继续执行。调用join()方法的线程将被阻塞,直到目标线程执行完毕。 通过以上方式,可以在Java中实现线程阻塞阻塞可以用于控制线程的执行顺序、提高程序的稳定性和效率。但要注意适当地使用阻塞,避免造成死锁或线程饥饿等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值