Android 面试必备 - 线程,android移动开发基础案例教程

本文详细介绍了Java中的线程同步机制,包括wait()、notify()方法和基于Lock接口的同步策略。通过生产者消费者问题的实例展示了wait()、notify()的使用,并对比了使用Lock和Condition实现的解决方案。此外,还讨论了线程生命周期、线程调度器等相关概念以及线程通信的限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LockA la = new LockA();

new Thread(la).start();

LockB lb = new LockB();

new Thread(lb).start();

}

}

class LockA implements Runnable{

public void run() {

try {

System.out.println(new Date().toString() + " LockA 开始执行");

while(true){

synchronized (LockTest.obj1) {

System.out.println(new Date().toString() + " LockA 锁住 obj1");

Thread.sleep(3000); // 此处等待是给B能锁住机会

synchronized (LockTest.obj2) {

System.out.println(new Date().toString() + " LockA 锁住 obj2");

Thread.sleep(60 * 1000); // 为测试,占用了就不放

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

class LockB implements Runnable{

public void run() {

try {

System.out.println(new Date().toString() + " LockB 开始执行");

while(true){

synchronized (LockTest.obj2) {

System.out.println(new Date().toString() + " LockB 锁住 obj2");

Thread.sleep(3000); // 此处等待是给A能锁住机会

synchronized (LockTest.obj1) {

System.out.println(new Date().toString() + " LockB 锁住 obj1");

Thread.sleep(60 * 1000); // 为测试,占用了就不放

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}


java 生产者消费者


生产者/消费者问题的多种Java实现方式

wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。

wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。

notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

光看文字可能不太好理解,咱来段代码就明白了:

import java.util.LinkedList;

/**

  • 仓库类Storage实现缓冲区

  • Email:530025983@qq.com

  • @author MONKEY.D.MENG 2011-03-15

*/

public class Storage

{

// 仓库最大存储量

private final int MAX_SIZE = 100;

// 仓库存储的载体

private LinkedList list = new LinkedList();

// 生产num个产品

public void produce(int num)

{

// 同步代码段

synchronized (list)

{

// 如果仓库剩余容量不足

while (list.size() + num > MAX_SIZE)

{

System.out.println("【要生产的产品数量】:" + num + “/t【库存量】:”

  • list.size() + “/t暂时不能执行生产任务!”);

try

{

// 由于条件不满足,生产阻塞

list.wait();

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

// 生产条件满足情况下,生产num个产品

for (int i = 1; i <= num; ++i)

{

list.add(new Object());

}

System.out.println("【已经生产产品数】:" + num + “/t【现仓储量为】:” + list.size());

list.notifyAll();

}

}

// 消费num个产品

public void consume(int num)

{

// 同步代码段

synchronized (list)

{

// 如果仓库存储量不足

while (list.size() < num)

{

System.out.println("【要消费的产品数量】:" + num + “/t【库存量】:”

  • list.size() + “/t暂时不能执行生产任务!”);

try

{

// 由于条件不满足,消费阻塞

list.wait();

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

// 消费条件满足情况下,消费num个产品

for (int i = 1; i <= num; ++i)

{

list.remove();

}

System.out.println("【已经消费产品数】:" + num + “/t【现仓储量为】:” + list.size());

list.notifyAll();

}

}

// get/set方法

public LinkedList getList()

{

return list;

}

public void setList(LinkedList list)

{

this.list = list;

}

public int getMAX_SIZE()

{

return MAX_SIZE;

}

}

/**

  • 生产者类Producer继承线程类Thread

  • Email:530025983@qq.com

  • @author MONKEY.D.MENG 2011-03-15

*/

public class Producer extends Thread

{

// 每次生产的产品数量

private int num;

// 所在放置的仓库

private Storage storage;

// 构造函数,设置仓库

public Producer(Storage storage)

{

this.storage = storage;

}

// 线程run函数

public void run()

{

produce(num);

}

// 调用仓库Storage的生产函数

public void produce(int num)

{

storage.produce(num);

}

// get/set方法

public int getNum()

{

return num;

}

public void setNum(int num)

{

this.num = num;

}

public Storage getStorage()

{

return storage;

}

public void setStorage(Storage storage)

{

this.storage = storage;

}

}

/**

  • 消费者类Consumer继承线程类Thread

  • Email:530025983@qq.com

  • @author MONKEY.D.MENG 2011-03-15

*/

public class Consumer extends Thread

{

// 每次消费的产品数量

private int num;

// 所在放置的仓库

private Storage storage;

// 构造函数,设置仓库

public Consumer(Storage storage)

{

this.storage = storage;

}

// 线程run函数

public void run()

{

consume(num);

}

// 调用仓库Storage的生产函数

public void consume(int num)

{

storage.consume(num);

}

// get/set方法

public int getNum()

{

return num;

}

public void setNum(int num)

{

this.num = num;

}

public Storage getStorage()

{

return storage;

}

public void setStorage(Storage storage)

{

this.storage = storage;

}

}

/**

  • 测试类Test

  • Email:530025983@qq.com

  • @author MONKEY.D.MENG 2011-03-15

*/

public class Test

{

public static void main(String[] args)

{

// 仓库对象

Storage storage = new Storage();

// 生产者对象

Producer p1 = new Producer(storage);

Producer p2 = new Producer(storage);

Producer p3 = new Producer(storage);

Producer p4 = new Producer(storage);

Producer p5 = new Producer(storage);

Producer p6 = new Producer(storage);

Producer p7 = new Producer(storage);

// 消费者对象

Consumer c1 = new Consumer(storage);

Consumer c2 = new Consumer(storage);

Consumer c3 = new Consumer(storage);

// 设置生产者产品生产数量

p1.setNum(10);

p2.setNum(10);

p3.setNum(10);

p4.setNum(10);

p5.setNum(10);

p6.setNum(10);

p7.setNum(80);

// 设置消费者产品消费数量

c1.setNum(50);

c2.setNum(20);

c3.setNum(30);

// 线程开始执行

c1.start();

c2.start();

c3.start();

p1.start();

p2.start();

p3.start();

p4.start();

p5.start();

p6.start();

p7.start();

}

}

在JDK5.0之后,Java提供了更加健壮的线程处理机制,包括同步、锁定、线程池等,它们可以实现更细粒度的线程控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait() / nofity()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。下面来看代码:

import java.util.LinkedList;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 仓库类Storage实现缓冲区

  • Email:530025983@qq.com

  • @author MONKEY.D.MENG 2011-03-15

*/

public class Storage

{

// 仓库最大存储量

private final int MAX_SIZE = 100;

// 仓库存储的载体

private LinkedList list = new LinkedList();

// 锁

private final Lock lock = new ReentrantLock();

// 仓库满的条件变量

private final Condition full = lock.newCondition();

// 仓库空的条件变量

private final Condition empty = lock.newCondition();

// 生产num个产品

public void produce(int num)

{

// 获得锁

lock.lock();

// 如果仓库剩余容量不足

while (list.size() + num > MAX_SIZE)

{

System.out.println("【要生产的产品数量】:" + num + “/t【库存量】:” + list.size()

  • “/t暂时不能执行生产任务!”);

try

{

// 由于条件不满足,生产阻塞

full.await();

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

// 生产条件满足情况下,生产num个产品

for (int i = 1; i <= num; ++i)

{

list.add(new Object());

}

System.out.println("【已经生产产品数】:" + num + “/t【现仓储量为】:” + list.size());

// 唤醒其他所有线程

full.signalAll();

empty.signalAll();

// 释放锁

lock.unlock();

}

// 消费num个产品

public void consume(int num)

{

// 获得锁

lock.lock();

// 如果仓库存储量不足

while (list.size() < num)

{

System.out.println("【要消费的产品数量】:" + num + “/t【库存量】:” + list.size()

  • “/t暂时不能执行生产任务!”);

try

{

// 由于条件不满足,消费阻塞

empty.await();

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

// 消费条件满足情况下,消费num个产品

for (int i = 1; i <= num; ++i)

{

list.remove();

}

System.out.println("【已经消费产品数】:" + num + “/t【现仓储量为】:” + list.size());

// 唤醒其他所有线程

full.signalAll();

empty.signalAll();

// 释放锁

lock.unlock();

}

// set/get方法

public int getMAX_SIZE()

{

return MAX_SIZE;

}

public LinkedList getList()

{

return list;

}

public void setList(LinkedList list)

{

this.list = list;

}

}

生产者与消费者模式在 Handler 中的体现

Android MessageQueue源码分析

Handler机制与生产者消费者模式


面试常见问题


1、进程和线程之间有什么不同?

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。

2、用户线程和守护线程有什么区别?

当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。

3、有哪些不同的线程生命期?

当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting,Blocked 和Dead。读这篇文章可以了解更多关于线程生命周期的知识。

4、什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

5、你如何确保main()方法所在的线程是Java程序最后结束的线程?

我们可以使用Thread类的join()方法来确保所有程序创建的线程在main()方法退出前结束。这里有一篇文章关于Thread类的joint()方法。

6、为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object类里?

一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。

7、 为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?

当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

8、 为什么Thread类的sleep()和yield()方法是静态的?

Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值