Simulate a pool using multithreading in Python
Naming

type is a built-in function in Python and therefore a name to avoid. Also, the widely followed Style Guide for Python Code aka PEP 8 recommends to use lower_case_with_underscores for variable names. CamelCase or more specifically TitleCase is usually "reserved" for class names.

Duplicate code

The behaviour function has an enormous amount of duplicated code. The code code for each of the if branches is basically the same, the only variable parts are the thresholds on which those branches are triggered and the amount of water that is either drained or added. Also the type parameter of behavior is redundant, since self already knows its type. Another thing that might need a closer look is the way you use the look. My threading is a bit rusty, but if I understood your code correctly, there is a separate lock for each instance of Pipe. As a consequence, there is no real synchronization between the threads and the happily check and modify Pipe.waterVolume as they go. But maybe I'm wrong here. Under the assumption that I'm right, let's look at a refactored version of your code:

class Pipe:
    water_volume = 0
    _lock = threading.Lock()

    def __init__(self, type_, flow, activation_condition):
        self.pipe_type = type_
        self._flow = flow
        self._activation_condition = activation_condition

    @classmethod
    def pipe1(cls):
        """A pipe of type 1 adding 100l when the volume is below 700"""
        return Pipe(1, 100, lambda volume: volume &lt; 700)

    @classmethod
    def pipe2(cls):
        """A pipe of type 2 adding 80l when the volume is below 700"""
        return Pipe(2, 80, lambda volume: volume &lt; 700)

    @classmethod
    def pipe3(cls):
        """A pipe of type 3 draining 110l when the volume is above 600"""
        return Pipe(3, -110, lambda volume: volume &gt; 600)

    def behaviour(self):
        while True:
            with Pipe._lock:
                if self._activation_condition(Pipe.water_volume):
                    Pipe.water_volume += self._flow
                    action = "added" if self._flow &gt; 0 else "drained"
                    print(f"Pipe {self.pipe_type+1} {action} {abs(self._flow)} liters")
                    print(f"Volume is: {Pipe.water_volume}")
            time.sleep(1.5)


A lot has changed here, so lets go through step-by-step.


I changed the naming to follow the PEP8 recommendation linked above.
The lock is now shared between all instances of the class (and their associated threads) the same way the water volume is.
The code duplication is gone. 


To reach that goal, the Pipe class was made more general in that it now accepts a flow rate (positive or negative) and a condition under which this pipe is active. To have to same "preconfigured" pipes as before, I added three classmethods which act as a simple factory. Each factory method also got a short description what to expect from the returned pipe.
behaviour (in lack of a better idea for a name) is now quite a bit shorter since it's basically the same code for all instances which add live to add via their properties. The lock is now also acquired before checking on the current volume. From a coding standpoint that absolutely makes sense, it's however up for discussion if a real system could/would be setup with this level of synchronization. The console output is now also generically built from what is known about the pipe object currently in action. I used f-strings (available from Python 3.6+) to format the output contrary to the "old-style" % formatting. Maybe have a look at this blog post to learn more about the different kinds of string formatting available to Python programmers.



Make it stop

I don't know if you have ever heard of the poem "Der Zauberlehrling" (The Sorcerer's Apprentice) by the German writer Johann Wolfgang von Goethe. In the poem, a young sorcerer summons a broom to help him carry water in order to clean his masters house. When he is done with the task, he tries to stop the broom from carrying more and more water into the house but fails to do so. From there on the events quickly spiral out of control until his master returns and finally takes control over the broom again.

Sounds vaguely familiar? Well, that's because once one has started your program, not even Ctrl+C will stop it (at least under Windows). This answer on SO could be used as a starting point to make that work.



A few closing words on threading in Python, especially in the most widely used reference implementation CPython. CPython has something called the global interpreter lock or GIL for short. The GIL takes care that at any time there is only a single thread executing Python code. That has significant impact on what you can expect to see when changing your pure Python code to multi-threading. Positive gains are mainly found when you have blocking IO operations or something like calls to a web API where the underlying code actively releases the GIL in order to allow other threads to do their work. Apart from that, the Python interpreter will also stop threads from time to time in order to allow over threads to run. When this happens depends on which Python version you are using (Python 2 vs. Python 3).

Since this is a topic that can quite involved, let me give you another link to a blog post on realpython and to a question/answer on Stack Overflow to read more about that topic.