Java多线程案例(一)
开发中线程的运行状态不固定,所以利用线程的名字和当前执行现成的对象进行区分
多个线程间也可能出现数据交互问题
以下介绍部分多线程问题的java实现
首先介绍以下同步和互斥的概念
同步和互斥的基本概念
互斥
互斥是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行,最基本的场景就是对资源的同时写,为了保持资源的一致性,往往需要进行互斥访问。
同步
同步是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务,最基本的场景就是任务之间的依赖,比如A任务的运行依赖于B任务产生的数据。
同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个任务之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!因此互斥具有唯一性和排它性,但互斥并不限制任务的运行顺序,即任务是无序的。而同步的任务之间则有顺序关系
生产者-消费者问题
生产者消费者模型中,生产者不断生产,消费者不断取走生产者生产的产品
由于线程运行的不确定性存在以下两种问题:
-
假设生产者线程向数据存储空间添加信息名称,还没有添加就切换到了消费者线程,消费者线程把刚存储的信息和上一个信息联系起来了
-
生产者存放了若干次数据,消费者才开始取数据
或者消费者取完一个数据后还没等生产者放入新数据,又重复取出已经取过的数据
生产者-消费者问题是操作系统中的一个著名进程同步的问题。这个问题用线程思想表达出来就是说有一些生产线程产生数据放置在公共区,一些消费线程去提取消费数据。在这个问题中要保护公共资源的安全性,在生产者生产物资时消费线程必须等待不能打断生产线程,当生产到一定数量时,生产线程暂停让消费线程提取数据。
如果不加入同步处理,程序会出现严重的问题:数据重复取出和重复设置
故应该使用同步控制
但是只使用同步的话会产生严重的数据重复问题
所以需要等待唤醒机制
Object类中定义的三个方法完成线程操作
public final void wait() throws InterruptedException //线程等待
public final void notify() //唤醒第一个等待线程
public final void notifyAll() //唤醒全部等待线程
等待的线程会排成一个等待序列
使用notify()会唤醒第一个等待的线程
notifyAll()可以唤醒所有线程, 谁的优先级高谁可能先执行
互斥操作
增加一个boolean变量标志位, 类比于操作系统中的mutex, 互斥量
这里的变量为true时可以生产, 但是不能取走, 消费者等待
变量为false时可以取走但是不能生产,生产者等待
示例代码
package thread_test.Producer_Consummer;
public class Message {
private String title;
private String content;
private boolean mutex = true;
public synchronized void set(String title,String content){
//当互斥量为false时可以取走但是不能生产,生产者等待
if(!this.mutex){
try{
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
this.mutex = false;
super.notify();
}
public synchronized void get(){
//当互斥量为true时可以生产, 但是不能取走, 消费者等待
if(this.mutex){
try{
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.title+"-->"+this.content);
this.mutex = true;
super.notify();
}
}
package thread_test.Producer_Consummer;
public class Producer implements Runnable{
private Message msg = null;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run(){
for (int x =0;x<10;x++){
if(x%2==0){
this.msg.set("阿伟","不要!");
}else {
this.msg.set("杰哥","让我康康!");
}
}
}
}
package thread_test.Producer_Consummer;
public class Consumer implements Runnable {
private Message msg = null;
public Consumer(Message msg){
this.msg = msg;
}
@Override
public void run(){
for (int x =0;x<10;x++){
this.msg.get();
}
}
}
package thread_test.Producer_Consummer;
public class Test {
public static void main(String args[])throws Exception{
Message msg = new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
由于java中同步方法的特性, 该程序实现要比操作系统中的PV操作方便很多