Louis Davidson shares some thoughts on exclusive locks in SQL Server:
You will find that the SELECT statement executes, ignoring the exclusive lock, because it is not a write lock, and the data on the page has not been changed.
The main reason people try to do this is to force access to a row in a single threaded manner. For example, building their own sequence number, either in a row they update, or by trying to do MAX() on all of the data in a table to make sure only one reader gets the same value.
This is generally a bad idea, since locking an entire table is a generally bad idea, but if you needed to block readers, you can couple the XLOCK with a PAGLOCK. So, change the first reader to:
BEGIN TRANSACTION;
SELECT *
FROM Demo.Test WITH (XLOCK,PAGLOCK);
As Louis points out in the summary, locking is complicated. Having a good understanding of the locking model will serve you well, though.