标签(空格分隔): java thread
简介:
随着操作系统的不断更新迭代,多线程编程已经变的十分常见,java虚拟机的多线程一般也是建立在操作系统本地native线程之上的,从而不必自己管理线程间的切换,直接交由本机操作系统进行调度和管理。对于java程序来说,它是运行在虚拟机上的,由于线程间很多资源都是共享的,比如全局数据等等,因此,线程间的同步也就不是那么复杂的。java也提供了许多实现线程同步的方法,我们这里主要分析wait和notify。
问题:
在一个盒子里,能放置一定数量的物体A,同一时间只能准许一个人往箱子里放物体或者一个人从盒子中取出物品。当盒子满了,再往里面放物体的人需要等待。当盒子空了,从盒子中取出物品的人需要等待。
这就是一个典型的生产者和消费者模型,下面在代码的基础上模拟这个过程。
- 盒子对象
public class DataManager {
private static final int LIMIT = 20;
private List<String> datas = new ArrayList<>();
public synchronized void put( String data ) {
while ( datas.size() == LIMIT ) {
// size limit, need wait.
try {
// 处于wait等待的线程会暂时释放锁,当其被唤醒时,会重新获得锁
wait();
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// 线程被唤醒后,重新检查条件
}
datas.add( data );
// 唤醒所有等待的线程
notifyAll();
}
public synchronized String get() {
while ( datas.size() == 0 ) {
// datas is empty, need wait.
try {
wait();
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
String data = datas.get( 0 );
datas.remove( 0 );
notifyAll();
return data;
}
}
DataManger展示了盒子这个模型,这里需要理解wait和notify以及notifyAll这几个方法。
2.生产者模型
public class Producer implements Runnable {
static int count = 0;
private DataManager mDataManager;
public Producer( DataManager dataManager ) {
mDataManager = dataManager;
}
@Override
public void run() {
while ( true ) {
try {
sleep( 100 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
String data = "data - " + count++;
mDataManager.put( data );
System.out.println( "Producer " + Thread.currentThread().getName() + ", put -> " + data );
}
}
}
3.消费者模型
public class Consumer implements Runnable {
private DataManager mDataManager;
public Consumer( DataManager dataManager ) {
mDataManager = dataManager;
}
@Override
public void run() {
while ( true ) {
try {
sleep( 100 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
String data = mDataManager.get();
System.out.println( "Consumer " + Thread.currentThread().getName() + ", get -> " + data );
}
}
}
Client端代码
public class Client {
public static void main( String[] args ) {
DataManager dataManager = new DataManager();
for ( int i = 0; i < 20; i++ ) {
new Thread( new Producer( dataManager ) ).start();
new Thread( new Consumer( dataManager ) ).start();
}
}
}
总结:
整体结构来说是比较简单的,所有生产者Producer和所有消费者Consumer共享同一个盒子DataManager。同时对盒子进行存取操作。对于上述代码,应该重点理解wait、notify以及notifyAll它们作用的对象,它们三者必须在同步块中使用,同时理解wait会释放对象锁,这点和sleep有区别,而被唤醒后又会重新持有对象锁,这个是线程同步的关键,也是理解这些代码的关键。当然java还有许多api提供了线程同步的方法,比如锁机制,其实两者在原理上是大同小异,都是基于本机操作系统的线程同步机制。
网友评论