Run the main function in File2 , the problem is : threads stuck at "rval=MVEL.executeExpression(compiledExpression, vars);" , 10 threads run in sequential order, not parallel , I wanna know why this happened.
PS: I'm using MVEL 2.2 , the latest version
File1:MVELHelper.java
public class MVELHelper {
private static ParserContext _ctx = new ParserContext(false);
//public static Object execute(String expression, Map<String, Object> vars, Databus databus) throws Exception {
public static Object execute(String expression, Map<String, Object> vars) throws Exception {
Object rval = null;
try {
if(vars == null) {
rval = MVEL.eval(expression, new HashMap<String,Object>());
}
else {
rval = MVEL.eval(expression, vars);
}
return rval;
}
catch(Exception e) {
throw new Exception("MVEL FAILED:"+expression,e);
}
}
public static Serializable compile(String text, ParserContext ctx)
throws Exception {
if(ctx == null) {
//ctx = _ctx;
ctx=new ParserContext(false);
}
Serializable exp = null;
try {
exp = MVEL.compileExpression(text, ctx);
//exp = MVEL.compileExpression(text);
}
catch (Exception e) {
throw new Exception("failed to compile expression.", e);
}
return exp;
}
public static Object compileAndExecute(String expression, Map<String, Object> vars) throws Exception {
Object rval = null;
try {
Serializable compiledExpression=compile(expression,null);
System.out.println("[COMPILE OVER, Thread Id="+Thread.currentThread().getId()+"] ");
if(vars == null) {
rval=MVEL.executeExpression(compiledExpression, new HashMap<String,Object>());
//rval = MVEL.eval(exp, new HashMap<String,Object>());
}
else {
//rval=MVEL.executeExpression(compiledExpression, vars,(VariableResolverFactory)null);
rval=MVEL.executeExpression(compiledExpression, vars);
//rval = MVEL.eval(expression, vars);
}
return rval;
}
catch(Exception e) {
throw new Exception("MVEL FAILED:"+expression,e);
}
}
}
File2:ExecThread3.java
public class ExecThread3 implements Runnable{
Map dataMap=null;
public Map getDataMap() {
return dataMap;
}
public void setDataMap(Map dataMap) {
this.dataMap = dataMap;
}
#Override
public void run() {
Map varsMap = new HashMap();
Map dataMap=new HashMap();
dataMap.put("count",100);
varsMap.put("dataMap", dataMap);
String expression="System.out.println(\"[BEFORE Thread Id=\"+Thread.currentThread().getId()+\"] \"+dataMap.get(\"count\"));"+
"Thread.sleep(3000);"+
"System.err.println(\"[AFTER Thread Id=\"+Thread.currentThread().getId()+\"] \"+dataMap.get(\"count\"));";
try {
//MVEL.compileExpression(expression);
MVELHelper.compileAndExecute(expression, varsMap);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
for(int k=0;k<10;k++){
ExecThread3 execThread=new ExecThread3();
new Thread(execThread).start();
}
}
}
Related
In the server,when receiving request login from every client,I will cache the data
using ConcurrentHashMap<String, String>()in the method channelRead,Can I get the value of the ConcurrentHashMap<String, String>() by defing a public methodpublic synchronized static Map<String, String> getClientMap() anywhere? And I want to define a schedule task to execute the opreation of clearing the cache whose data in the above ConcurrentHashMap<String, String>(),how can I do in Netty? I try many times ,but failed.
This is my code in ChannelHandler:
public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthRespHandler.class);
private static final Map<String, String> nodeCheck = new ConcurrentHashMap<String, String>();
private String[] whiteList = { "127.0.0.1", "192.168.56.1" };
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
AlarmMessage message = (AlarmMessage) msg;
//judge the messsge's type
if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_REQ.value()) {
String nodeIndex = ctx.channel().remoteAddress().toString();
AlarmMessage loginResp = null;
if (nodeCheck.containsKey(nodeIndex)) {
loginResp = buildResponse(ResultType.FAIL);
} else {
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
String ip = address.getAddress().getHostAddress();
boolean isOK = false;
for (String WIP : whiteList) {
if (WIP.equals(ip)) {
isOK = true;
break;
}
}
loginResp = isOK ? buildResponse(ResultType.SUCCESS) : buildResponse(ResultType.FAIL);
if (isOK)
//add a value
nodeCheck.put(nodeIndex, message.getBody().toString());
System.out.println(nodeCheck.get(nodeIndex));
}
ctx.writeAndFlush(loginResp);
} else {
ctx.fireChannelRead(msg);
}
}
private AlarmMessage buildResponse(ResultType result) {
AlarmMessage message = new AlarmMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_RESP.value());
message.setHeader(header);
message.setBody(result.value());
return message;
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String nodeIndex = ctx.channel().remoteAddress().toString();
ctx.close();
if(nodeCheck.containsKey(nodeIndex)){
nodeCheck.remove(nodeIndex);
}
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
ctx.fireExceptionCaught(cause);
}
public static Map<String, String> getNodeCheck() {
return nodeCheck;
}
}
And this is my code in main thread:
ScheduledFuture<?> sf = executor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
HashSet<String> clients = new HashSet<>();
Map<String,String> map = LoginAuthRespHandler.getNodeCheck();
System.out.println(map.size());
for (String key:map.keySet()) {
clients.add(map.get(key));
}
//update data
try{
MySQLDB.updateClientStatus(clients);
}catch (Exception e){
e.printStackTrace();
}
//clear map
map.clear();
clients.clear();
}
},10,10,TimeUnit.SECONDS);
}
My app is saving a hashmap before it stops and when it starts again loads the same hashmap so changes could be made to it. I am using Serialization.
Storage class:
public class Storage {
private Map<String, String> storage;
private String projectStorageFilePath;
public Storage() {
this.storage = new ConcurrentHashMap<String, String>();
makeDir();
}
/**
* If the file in which the map objects will be saved doesn't exist in the
* user home directory it creates it.
*/
private void makeDir() {
File projectHomeDir = new File(System.getProperty("user.home"), ".TestMap");
String projectHomeDirPath = projectHomeDir.getAbsolutePath();
File projectStorageFile = new File(projectHomeDirPath, "storage.save");
projectStorageFilePath = projectStorageFile.getAbsolutePath();
if (!projectHomeDir.exists()) {
projectHomeDir.mkdir();
try {
projectStorageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("unchecked")
public boolean load() {
boolean isLoaded = false;
ObjectInputStream ois = null;
try {
File file = new File(projectStorageFilePath);
if (file.length() != 0) {
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage = (ConcurrentHashMap<String, String>) ois.readObject();
isLoaded = true;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null != ois) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isLoaded;
}
public boolean save() {
boolean isSaved = false;
ObjectOutputStream oos = null;
try {
//saving
oos = new ObjectOutputStream(new FileOutputStream(projectStorageFilePath));
oos.writeObject(storage);
isSaved = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != oos) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isSaved;
}
public Map<String, String> getStorage() {
return this.storage;
}
}
The class in which I am trying to do something with that hashmap:
public class DoSomethingWithMap {
private Map<String, String> storage;
public DoSomethingWithMap(Map<String, String> storage) {
this.storage = storage;
}
public void addToMap(String key, String value) {
this.storage.put(key, value);
}
public void printMap() {
System.out.println(this.storage);
}
}
When I run it the first time it works fine:
public class Main {
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.addToMap("2", "test2");
something.addToMap("4", "test4");
something.addToMap("5", "test5");
if (s.save()) {
System.out.println(s.getStorage());
}
}
}
Output:
{} //empty map which is ok because it has never been saved before
{3=test3, 4=test4, 5=test5} //changes during runtime are saved
The problem is when I start Main again and try to make changes to the saved map:
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.printMap();
something.addToMap("6", "newTest");
something.addToMap("7", "newTest");
something.addToMap("8", "newTest");
something.printMap();
if (s.save()) {
System.out.println(s.getStorage());
}
}
Output:
{3=test3, 4=test4, 5=test5} //loading the map works fine
{} //here it should be same as previous line but is not
{6=newTest, 7=newTest, 8=newTest} //DoSomethingWithMap.printMap is printing only the changes during runtime
{3=test3, 4=test4, 5=test5} // changes during runtime are not saved
It is obvious DoSomethingWithMap class is not using the map which was given to it. Why? Which map is using? How I can fix that?
Thank you.
You are creating a new instance of the Map in your load method:
storage = (ConcurrentHashMap<String, String>) ois.readObject();
To fix you can clear the current map and then add all the values from the loaded one:
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage.clear();
storage.putAll((ConcurrentHashMap<String, String>) ois.readObject());
To prevent such error in the future, you could make those fields final and thus you will get error reports.
Here is my demo:
PoolableObjectFactoryImpl.java
public class PoolableObjectFactoryImpl implements PoolableObjectFactory<Result> {
private static Logger logger = Logger.getLogger("BackgroundLog");
#Override
public void activateObject(Result obj) throws Exception {
logger.info("==activate result.==");
obj.setResult(-999);
}
#Override
public void destroyObject(Result obj) throws Exception {
logger.info("==destroy result.==");
obj = null;
}
#Override
public Result makeObject() throws Exception {
logger.info("==make result.==");
Result result = new Result();
return result;
}
#Override
public void passivateObject(Result obj) throws Exception {
logger.info("==passivate result.==");
obj.setResult(-999);
}
#Override
public boolean validateObject(Result obj) {
/*if(obj.getResult() == -999){
logger.info("==validate result true.==");
return true;
}else{
logger.info("==validate result false.==");
return false;
}*/
logger.info("==validate result true.==");
return true;
}
}
ThreadPool.java
public class ThreadPool extends GenericObjectPool {
private static Logger logger = Logger.getLogger("BackgroundLog");
private static ThreadPool pool = null;
private Map<String, String> map = getConfig();
private ThreadPool() {
this.setFactory(new PoolableObjectFactoryImpl());
this.setMaxActive(Integer.parseInt(map.get("maxActive")));
this.setWhenExhaustedAction(Byte.valueOf(map.get("whenExhaustedAction")));
this.setMaxWait(Long.parseLong(map.get("maxWait")));
this.setMaxIdle(Integer.parseInt(map.get("maxIdle")));
this.setTestOnBorrow(Boolean.valueOf(map.get("testOnBorrow")));
this.setTestOnReturn(Boolean.valueOf(map.get("testOnReturn")));
this.setTimeBetweenEvictionRunsMillis(Long.parseLong(map.get("timeBetweenEvictionRunsMillis")));
this.setNumTestsPerEvictionRun(Integer.parseInt(map.get("numTestsPerEvictionRun")));
this.setMinEvictableIdleTimeMillis(Long.parseLong(map.get("minEvictableIdleTimeMillis")));
this.setTestWhileIdle(Boolean.valueOf(map.get("testWhileIdle")));
}
public static ThreadPool getInstance() {
if (pool == null) {
synchronized (ThreadPool.class) {
if (pool == null) {
logger.info("thread pool is initialized.");
pool = new ThreadPool();
}
}
}
return pool;
}
/**
*
* <p>Title: getConfig</p>
* <p>Description: get pool configuration</p>
* #return
*/
public Map<String, String> getConfig() {
Map<String, String> map = new HashMap<String, String>();
Properties props = new Properties();
try {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("pool.properties");
props.load(in);
Enumeration en = props.propertyNames();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
map.put(key, props.getProperty(key));
}
in.close();
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
return map;
}
}
Result.java
public class Result {
private int result;
public Result(){
}
public int getResult(){
return this.result;
}
public void setResult(int result){
this.result = result;
}
}
Test.java
public class Test implements Runnable {
private static Logger logger = Logger.getLogger("BackgroundLog");
private String name = null;
public Test(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
#Override
public void run() {
ThreadPool pool = ThreadPool.getInstance();
for(int i=0;i<1000;i++){
try {
Result result = (Result)pool.borrowObject();
logger.info("numActive: "+ pool.getNumActive()+"\t"+"numIdle: "+pool.getNumIdle());
logger.info("thread "+getName()+" "+i+" borrow object from pool "+result.getResult()+".");
result.setResult(0);
pool.returnObject(result);
logger.info("return object to pool.");
Thread.sleep(100);
} catch (Exception e) {
logger.info("thread "+getName()+" "+i);
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for(int i=0;i<50;i++){
Thread t = new Thread(new Test("t"+i));
t.start();
}
}
}
Next is the configuration properties:
Next is the threads view from Jprofiler when it has 4 threads:
After Test.java is running a few minutes,some threads keep beling blocked,only one is still running but does not print any log.I don't really understand thread thing.
can anyone explain why? how to avoid threads being blocked?
Consider posting logs of an execution cycle.
Did you try commenting Thread.sleep line, because sleep will hold onto the lock it has acquired till the thread is in sleep mode.
Try replacing "Thread.sleep(100);" with:
try {
synchronized (this) {
this.wait(200);
}
} catch (InterruptedException e) {
}
What is advantage of locks over wait/notify?
Code is very similar.
private Object full = new Object();
private Object empty = new Object();
private Object data = null;
public static void main(String[] args) {
Test test = new Test();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
}
public void push(Object d) {
synchronized (full) {
while (data != null)
try {
full.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data = d;
System.out.println("push");
synchronized (empty) {
if (data != null)
empty.notify();
}
}
public Object pop() {
synchronized (empty) {
while (data == null)
try {
empty.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object o = data;
data = null;
System.out.println("pop");
synchronized (full) {
if (data == null)
full.notify();
}
return o;
}
class Producer implements Runnable {
public void run() {
while (true) {
push(new Object());
}
}
}
class Consumer implements Runnable {
public void run() {
while (true) {
pop();
}
}
}
and
private final ReentrantLock lock = new ReentrantLock();
private final Condition fullState = lock.newCondition();
private final Condition emptyState = lock.newCondition();
private Object data = null;
public static void main(String[] args) {
Test test = new Test();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
}
public void push(Object d) {
lock.lock();
try {
while (data != null)
try {
fullState.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
data = d;
System.out.println("push");
emptyState.signal();
} finally {
lock.unlock();
}
}
public Object pop() {
Object result;
lock.lock();
try {
while (data == null)
try {
emptyState.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
result = data;
data = null;
System.out.println("pop");
fullState.signal();
} finally {
lock.unlock();
}
return result;
}
class Producer implements Runnable {
public void run() {
while (true) {
push(new Object());
}
}
}
class Consumer implements Runnable {
public void run() {
while (true) {
pop();
}
}
}
Check out the JavaDoc for ReeentratLock and your question will be answered.
"A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities."
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html
I have a program that performs lots of calculations and reports them to a file frequently. I know that frequent write operations can slow a program down a lot, so to avoid it I'd like to have a second thread dedicated to the writing operations.
Right now I'm doing it with this class I wrote (the impatient can skip to the end of the question):
public class ParallelWriter implements Runnable {
private File file;
private BlockingQueue<Item> q;
private int indentation;
public ParallelWriter( File f ){
file = f;
q = new LinkedBlockingQueue<Item>();
indentation = 0;
}
public ParallelWriter append( CharSequence str ){
try {
CharSeqItem item = new CharSeqItem();
item.content = str;
item.type = ItemType.CHARSEQ;
q.put(item);
return this;
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public ParallelWriter newLine(){
try {
Item item = new Item();
item.type = ItemType.NEWLINE;
q.put(item);
return this;
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void setIndent(int indentation) {
try{
IndentCommand item = new IndentCommand();
item.type = ItemType.INDENT;
item.indent = indentation;
q.put(item);
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void end(){
try {
Item item = new Item();
item.type = ItemType.POISON;
q.put(item);
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void run() {
BufferedWriter out = null;
Item item = null;
try{
out = new BufferedWriter( new FileWriter( file ) );
while( (item = q.take()).type != ItemType.POISON ){
switch( item.type ){
case NEWLINE:
out.newLine();
for( int i = 0; i < indentation; i++ )
out.append(" ");
break;
case INDENT:
indentation = ((IndentCommand)item).indent;
break;
case CHARSEQ:
out.append( ((CharSeqItem)item).content );
}
}
} catch (InterruptedException ex){
throw new RuntimeException( ex );
} catch (IOException ex) {
throw new RuntimeException( ex );
} finally {
if( out != null ) try {
out.close();
} catch (IOException ex) {
throw new RuntimeException( ex );
}
}
}
private enum ItemType {
CHARSEQ, NEWLINE, INDENT, POISON;
}
private static class Item {
ItemType type;
}
private static class CharSeqItem extends Item {
CharSequence content;
}
private static class IndentCommand extends Item {
int indent;
}
}
And then I use it by doing:
ParallelWriter w = new ParallelWriter( myFile );
new Thread(w).start();
/// Lots of
w.append(" things ").newLine();
w.setIndent(2);
w.newLine().append(" more things ");
/// and finally
w.end();
While this works perfectly well, I'm wondering:
Is there a better way to accomplish this?
Your basic approach looks fine. I would structure the code as follows:
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public interface FileWriter {
FileWriter append(CharSequence seq);
FileWriter indent(int indent);
void close();
}
class AsyncFileWriter implements FileWriter, Runnable {
private final File file;
private final Writer out;
private final BlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();
private volatile boolean started = false;
private volatile boolean stopped = false;
public AsyncFileWriter(File file) throws IOException {
this.file = file;
this.out = new BufferedWriter(new java.io.FileWriter(file));
}
public FileWriter append(CharSequence seq) {
if (!started) {
throw new IllegalStateException("open() call expected before append()");
}
try {
queue.put(new CharSeqItem(seq));
} catch (InterruptedException ignored) {
}
return this;
}
public FileWriter indent(int indent) {
if (!started) {
throw new IllegalStateException("open() call expected before append()");
}
try {
queue.put(new IndentItem(indent));
} catch (InterruptedException ignored) {
}
return this;
}
public void open() {
this.started = true;
new Thread(this).start();
}
public void run() {
while (!stopped) {
try {
Item item = queue.poll(100, TimeUnit.MICROSECONDS);
if (item != null) {
try {
item.write(out);
} catch (IOException logme) {
}
}
} catch (InterruptedException e) {
}
}
try {
out.close();
} catch (IOException ignore) {
}
}
public void close() {
this.stopped = true;
}
private static interface Item {
void write(Writer out) throws IOException;
}
private static class CharSeqItem implements Item {
private final CharSequence sequence;
public CharSeqItem(CharSequence sequence) {
this.sequence = sequence;
}
public void write(Writer out) throws IOException {
out.append(sequence);
}
}
private static class IndentItem implements Item {
private final int indent;
public IndentItem(int indent) {
this.indent = indent;
}
public void write(Writer out) throws IOException {
for (int i = 0; i < indent; i++) {
out.append(" ");
}
}
}
}
If you do not want to write in a separate thread (maybe in a test?), you can have an implementation of FileWriter which calls append on the Writer in the caller thread.
One good way to exchange data with a single consumer thread is to use an Exchanger.
You could use a StringBuilder or ByteBuffer as the buffer to exchange with the background thread. The latency incurred can be around 1 micro-second, doesn't involve creating any objects and which is lower using a BlockingQueue.
From the example which I think is worth repeating here.
class FillAndEmpty {
Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
DataBuffer initialEmptyBuffer = ... a made-up type
DataBuffer initialFullBuffer = ...
class FillingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null) {
addToBuffer(currentBuffer);
if (currentBuffer.isFull())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ... }
}
}
class EmptyingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.isEmpty())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ...}
}
}
void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
Using a LinkedBlockingQueue is a pretty good idea. Not sure I like some of the style of the code... but the principle seems sound.
I would maybe add a capacity to the LinkedBlockingQueue equal to a certain % of your total memory.. say 10,000 items.. this way if your writing is going too slow, your worker threads won't keep adding more work until the heap is blown.
I know that frequent write operations
can slow a program down a lot
Probably not as much as you think, provided you use buffering.