Prior VHDL 1993, two concurrent processes could communicate only with signals. Thanks to the simulation semantics of the language that updates signals only between simulation steps, the result of a simulation was deterministic: it did not depend on the order chosen by the simulation scheduler to execute the processes.
[In fact, this is not 100% true. Processes could also communicate using file input/output. But if a designer was compromising the determinism by using files, it could not really be a mistake.]
The language was thus safe. Except on purpose, it was almost impossible to design non-deterministic VHDL models.
VHDL 1993 introduced shared variables and designing non-deterministic VHDL models became very easy.
VHDL 2000 introduced protected types and the constraint that shared variables must be of protected type.
In VHDL, protected types are what resemble most the concept of objects in Object Oriented (OO) languages. They implement the encapsulation of data structures and their methods. They also guarantee the exclusive and atomic access to their data members. This does not completely prevent non-determinism but, at least, adds exclusiveness and atomicity to the shared variables.
Protected types are very useful when designing high level VHDL models intended for simulation only. They have several very good properties of OO languages. Using them frequently makes the code more readable, maintainable and reusable.