I am working on a web application, security is high priority. So, if a user has failed to login more than 3 times, the account will be locked. Should I display a message to the user saying "your account has been locked"? It would help to attacker to get the username.
You must tell the user that the account has been locked, otherwise I don't see how you would handle a such situation without displaying a message directly in the web application. Of course, you would most-likely send him an e-mail with some steps to unlock his account but still, you must notify the user within the application that you've sent him an e-mail.
Now, considering your main concern which is that an attacker could build a list of the registered usernames on your website, there is a solution to mitigate this problem.
Tracking attempts by cookies/hidden fields/database entries
Whenever a user attempts to login using a non-existent username, you could keep track of his failed attempt by storing the information in a cookie, a hidden field or even directly in your database. That way, if you would lock a real account after 3 failed attempts, you could keep track of the failed attempts (which aren't real failed attempts because the username doesn't really exist) and display the lock message exactly like in a real situation.
The most transparent and efficient way to implement this would be by inserting the failed attempts in a database because the user can always modify the hidden fields and the cookies, which could eventually help him to figure your little ruse. Also, it might be hard to keep track of the non-existent username attempts if the user tries many usernames by using cookies and hidden fields. However, in the database, you could simply log the user's IP, the non-existent username and finally the amount of failed attempts which would tell if you have to display the fake lock message or not to your web application.
You might also not want to filter attempts only by IP, in case the attacker uses a proxy and tests the very same username and notices that it took him again, for example, 3 attempts to lock the account. Again, he could figure out your ruse like this.
But, do you really need a such protection?
Before implementing a such protection, take some time to think if it is really necessary for your web application. A situation where this would be completely useless is a website that has a forum where users login using the very same username as the one displayed on the said forum. Obviously, a malicious user could build a list of the registered users quite easily simply by browsing pages.
Login using e-mail address
Have you thought of using the user's e-mail address to connect to your web application instead, if possible? It is far less likely that someone will attempt to build a list of e-mail addresses as there are way more possibilities than usernames (users tend to choose typical and simple usernames such as "Simon" when it is available. However, finding Simon's e-mail address is harder, as there are many possible e-mail services assuming he chose the username "Simon" on it again (e.g. Simon@gmail.com, Simon@hotmail.com, Simon@yahoo.com, etc.)).
While @Simon's answer is excellent, I would like to add one additional point. Do not lock out user accounts due to failed login attempts.
By doing so, you are introducing a huge DoS vulnerability in your application. Any malicious attacker can simply lock out a large number of your users by repeatedly failing the login process. You will also be introducing a large support headache because users will fail the login process. Sometimes repeatedly. Remember the third facet of the CIA triangle, Availability. Your application is useless if no one can use it.
Instead, what you should do is throttle the amount of login attempts allowed. Introduce a "time-out" period instead of permanently locking the user's account pending some action on the users part. This will sufficiently stop online bruteforce attacks while keeping the support emails and calls to a sane level.
If you're getting a lot of password attempts, you need to lock out the attacker, not the victim. This should be obvious, but it's a pretty common problem anyway.
First of all, slow the attack. After even a single incorrect password attempt, the IP that sent the attempt shouldn't be able to submit another until several seconds have passed. You can mask this on the browser-side by displaying a "authenticating, please wait..." message. But on the backend, if you get more than one attempt in a single N-second span from a given IP, then return an error message that lets the attacker know you're not even going check to see if his guess was right.
For bonus points, require N seconds of silence, not just N seconds since the first guess. That way beating on the server only extends the timeout.
Second, block offending IPs, not offended accounts. You can easily extend your solution from the previous point to increase the wait time after N failed attempts. So for example, 10 attempts and you get an additional 15 minutes in the penalty box. You could even show a friendly countdown timer on your webpage. It's also helpful to tell people "3 tries left" so they think about what they type, and don't just hammer away on bad passwords till they get locked out.
Third, respond to attacks by making it less bot-friendly. If a single account is getting attention from hundreds of IPs, you change things around to break the bots. Put up a CAPTCHA challenge, for example. Real users may find it annoying, but not nearly as annoying as being locked out of your account.
Not showing error message doesn't add much value for security, while is a constant bad user experience, because it is very convenient to the user to know whats wrong with the login input. If your system require high security, you can use a timeout punishment system and a better password frame.