您的位置:首页 > 其它

Servlet参数持久化时可能遇到的多线程问题以及解决之道

2006-08-14 14:29 651 查看
 Up until now, the counter examples have demonstrated how servlet state persists between accesses. This solves only part of the problem. Every time the server is shut down or the servlet is reloaded, the count begins again. What we really want is persistence across loads—a counter that doesn't have to start over.
The init( ) and destroy( ) pair can accomplish this. Example 3-5 further extends the InitCounter example, giving the servlet the ability to save its state in destroy( ) and load the state again in init( ). To keep things simple, assume this servlet is not registered and is accessed only as http://server:port/servlet/InitDestroyCounter. If it were registered under different names, it would have to save a separate state for each name.

Example 3-5. A Fully Persistent Counter

Each time this servlet is unloaded, it saves its state in a file named InitDestroyCounter.initial. In the absence of a supplied path, the file is saved in the server process's current directory, usually the startup directory. Ways to specify alternate locations are discussed in Chapter 4.[4] This file contains a single integer, saved as a string, that represents the latest count.

[4] The location of the current user directory can be found with System.getProperty("user.dir").

Each time the servlet is loaded, it tries to read the saved count
4000
from the file. If, for some reason, the read fails (as it does the first time the servlet runs because the file doesn't yet exist), the servlet checks if an init parameter specifies the starting count. If that too fails, it starts fresh with zero. You can never be too careful in init( ) methods.

Servlets can save their state in many different ways. Some may use a custom file format, as was done here. Others may save their state as serialized Java objects or put it into a database. Some may even perform journaling, a technique common to databases and tape backups, where the servlet's full state is saved infrequently while a journal file stores incremental updates as things change. Which method a servlet should use depends on the situation. In any case, you should always be watchful that the state being saved isn't undergoing any change in the background.

Right now you're probably asking yourself, "What happens if the server crashes?" It's a good question. The answer is that the destroy( ) method will not be called.[5]

[5] Unless you're so unlucky that your server crashes while in the destroy( ) method. In that case, you may be left with a partially written state file—garbage written on top of your previous state. To be perfectly safe, a servlet should save its state to a temporary file and then copy that file on top of the official state file in one command.

This doesn't cause a problem for destroy( ) methods that only have to free resources; a rebooted server does that job just as well (if not better). But it does cause a problem for a servlet that needs to save its state in its destroy( ) method. For these servlets, the only guaranteed solution is to save state more often. A servlet may choose to save its state after handling each request, such as a "chess server" servlet should do, so that even if the server is restarted, the game can resume with the latest board position. Other servlets may need to save state only after some important value has changed—a "shopping cart" servlet needs to save its state only when a customer adds or removes an item from her cart. Last, for some servlets, it's fine to lose a bit of the recent state changes. These servlets can save state after some set number of requests. For example, in our InitDestroyCounter example, it should be satisfactory to save state every 10 accesses. To implement this, we can add the following line at the end of doGet( ):


Does this addition make you cringe? It should. Think about synchronization issues. We've opened up the possibility for data loss if saveState( ) is executed by two threads at the same time and the possibility for saveState( ) to not be called at all if count is incremented by several threads in a row before the check. Note that this possibility did not exist when saveState( ) was called only from the destroy( ) method: the destroy( ) method is called just once per servlet instance. Now that saveState( ) is called in the doGet( ) method, however, we need to reconsider. If by some chance this servlet is accessed so frequently that it has more than 10 concurrently executing threads, it's likely that two servlets (10 requests apart) will be in saveState( ) at the same time. This may result in a corrupted datafile. It's also possible the two threads will increment count before either thread notices it was time to call saveState( ). The fix is easy: move the count check into the synchronized block where count is incremented:


The moral of the story is harder: always be vigilant to protect servlet code from multithreaded access problems.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐