java的线程读写操作(共享跟互斥)
java的线程读写操作(共享和互斥)
最近看了看java的线程操作的方法的内容,照书上写了一下代码试了试,主要的内容是我们固定输入一段字符串,然后依次按顺序读每一个字符,创建6个读操作的线程和2个写进程的进程,操作的方式是,可以同时读,但是不可以同时写,而且在读的时候不可以写,写的时候不可以读!
erlang多是利用同步异步消息来处理数据问题,所以有的时候自己经常会忘记锁了以后要解锁!!!!
package test1; public class Main { public static void main(String[] args){ Data data = new Data(10); //创建Data类的实例 /**启动6个读操作线程**/ new ReaderThread(data).start(); new ReaderThread(data).start(); new ReaderThread(data).start(); new ReaderThread(data).start(); new ReaderThread(data).start(); new ReaderThread(data).start(); /**启动2个写操作线程**/ new WriterThread(data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ").start(); new WriterThread(data, "abcdefghijklmnopqrstuvwxyz").start(); } }
package test1; /**数据操作类**/ public class Data { private final char[] buffer; //新建一个缓存字符串数组 private final ReadWriteLock lock = new ReadWriteLock(); //创建一个ReadWriteLock类的对象lock /**Data类的构造函数,输入一个size**/ public Data(int size){ this.buffer = new char[size]; //为buffer对象申请size大小的空间 for(int i = 0; i < buffer.length; i++){ buffer[i] = '*'; //将buffer内初始为*号 } } /**读操作方法(主要完成的是锁操作)**/ public char[] read() throws InterruptedException{ lock.readLock(); //对lock实例进行读锁操作 try{ return doRead(); //读操作 }finally{ lock.readUnlock(); //对lock实例读解锁操作 } } /**写操作方法(主要完成的是锁操作)**/ public void write(char c) throws InterruptedException{ lock.writeLock(); //对lock实例进行写锁操作 try{ doWrite(c); //进行写操作 }finally{ lock.writeUnlock(); //进行写解锁 } } /**读操作主要逻辑**/ private char[] doRead(){ char[] newbuf = new char[buffer.length]; //创建一个大小为buffer.length的newbuf对象 for(int i = 0; i < buffer.length; i++){ newbuf[i] = buffer[i]; //数据拷贝 } slowly(); //延迟操作 return newbuf; //返回newbuf } /**写操作主要逻辑**/ private void doWrite(char c){ for(int i = 0; i < buffer.length; i++){ buffer[i] = c; //将buffer中的数据替换成字符串c slowly(); //延迟操作 } } /**模拟时间操作**/ private void slowly(){ try{ Thread.sleep(0); //延迟50毫秒 }catch(InterruptedException e){ } } }
package test1; /**读写锁操作类**/ public final class ReadWriteLock { private int readingReaders = 0; //阅读者的数量 private int waitingWriters = 0; //等待的编辑者数量 private int writingWriters = 0; //编辑者的数量 private boolean preferWriter = true; //是否正在被编辑 /**读锁**/ public synchronized void readLock() throws InterruptedException{ //如果是 编写者的数量大于0 并且 等待的编写者大于0或者正在被编写 那么该进程等待 while(writingWriters > 0 || (preferWriter && waitingWriters >0)){ //等待操作 wait(); } //读写者加1可以读 readingReaders++; } /**读解锁**/ public synchronized void readUnlock(){ readingReaders--; //读者减1 解锁读 preferWriter = true; //可以写 notifyAll(); //唤醒其他进程操作 } /**写锁**/ public synchronized void writeLock() throws InterruptedException{ waitingWriters++; //编辑者加1 try{ //如果读者大于0 并且 编写者大于0 while(readingReaders > 0 || writingWriters > 0){ //等待操作 wait(); } }finally{ waitingWriters--; //最终等待者减1 } writingWriters++; //编写者加1 } /**编写者解锁**/ public synchronized void writeUnlock(){ writingWriters --; //编辑者减1 preferWriter = false; //没有被编辑 notifyAll(); //唤醒其他的进程 } }
package test1; import java.util.Random; /**写操作线程类**/ public class WriterThread extends Thread { private static final Random random = new Random(); //random的实例 private final Data data; //创建一个Data的实例 private final String filler; //创建一个filler的字符串 private int index = 0; //创建一个index类为0 /**WriterThread类构造器**/ public WriterThread(Data data, String filler){ this.data = data; this.filler = filler; } /**启动线程实现run方法**/ public void run(){ try{ while(true){ char c = nextchar(); //返回filler中的下一个字符 data.write(c); //将字符c写入 Thread.sleep(random.nextInt(3000)); //1~3000随机休眠 } }catch (InterruptedException e){ } } /**实现nextchar的方法,指定索引位置的char值**/ private char nextchar(){ char c = filler.charAt(index); //字符串的位置 index++; //index的值递增 if(index >= filler.length()){ index = 0; //如果index的值大于字符串的长度则index为0 } return c; } }
package test1; /**读操作的线程类**/ public class ReaderThread extends Thread{ private final Data data; //创建一个Data的实例 /**该类的构造器**/ public ReaderThread(Data data){ this.data = data; } /**实现run方法**/ public void run(){ try{ while(true){ char[] readbuf = data.read(); //读操作 System.out.println(Thread.currentThread().getName() + "reads" + String.valueOf(readbuf)); //打印操作 } }catch (InterruptedException e){ } } }
这个程序虽然只是简单的多线程编程的一个例子,但是只要将main函数以及模拟休息的部分和输入的buffer参数稍作修改就可以完成一些真正意义上的并发锁的操作
这里还是有些要注意的:
1.你想要保护什么就锁什么!
2.完成操作一定要解锁!
测试例子:(比较多,总之在读完所有的字符以前不出现混乱读取)