Fundamentally, a core goal in rendering is for each frame displayed on the monitor to present a single, coherent image. There are several different strategies which are, or were, used in order to achieve this.
In the following, I mention "vsync". Vsync is the moment at which the monitor begins drawing a new screen image; it's the point at which "vblank" begins on a traditional CRT screen, where the scanline momentarily stops drawing and moves back to the top of the monitor. This moment is very important for many of the approaches to frame coherence.
"Tearing" is what we call it when a screen renders from two different images, within a single frame. If, for example, I've drawn two screen images which are intended to be displayed one after the other, but the monitor has instead displayed the top half of frame one, and the bottom half of frame two, that's "tearing". This happens due to changing the data the monitor is reading from while the monitor is drawing, instead of during vblank. (In modern programs, this typically happens because the user disabled waiting for vsync in their driver settings)
On the oldest hardware, there often wasn't enough memory to hold a full screen image, and so instead of drawing a screen image, you needed to specify colors for each scanline individually, while the monitor was in the process of drawing that line. On the Atari 2600, for example, you had just 76 machine instruction cycles to specify what color went into each pixel of the scanline, before the television started to actually draw that scanline. And then you had 76 instruction cycles to provide the contents for the next scanline, and so on.
When drawing in a "single-buffer" context, you're drawing straight into the VRAM which is being read from by the monitor. In this approach, you "race the scanline". The general idea is that when the scanline starts drawing the previous frame's contents at the top of the screen, you draw into VRAM behind it. So while the scanline is drawing the screen image for the last frame, you're drawing the next frame behind the scanline.
In general, you're trying to finish drawing the next-frame image before the scanline "laps" you by coming around again and overtaking the pixels that you're drawing, and also to never get ahead of the scanline, or else your new frame might draw into what should have been the previous frame.
For this reason, single-buffer rendering typically worked itself by drawing scanlines, from top to bottom, and from left to right. If you drew in some other order, it was likely that the scanline would come around again and spot bits of the "next" image which you hadn't gotten around to drawing yet.
Note that in modern operating systems, you typically never have an opportunity to draw single-buffered, though this was fairly common thirty years ago. (gosh do I feel old right now -- this is what I was doing when I first started out in game development)
This is much, much simpler than either of the strategies that came before.
In a double-buffering system, we have enough memory to store two different screen images, and so we designate one of them as the "front buffer" and the other, the "back buffer". The "front buffer" is what's currently being displayed, and the "back buffer" is where we're currently drawing.
After we finish drawing a screen image to the back buffer, we wait until vsync, and then swap the two buffers. This way, the back buffer becomes the front buffer, and vice versa, and the whole swap happened while the monitor wasn't drawing anything.
One issue often raised with double-buffer approaches is that after we finish drawing to the back buffer, we have to just sit around waiting for vsync before we can swap the buffers and continue working; we could have been doing calculations during that time! What's more, all the time that we're waiting to swap between the buffers, the image in that back buffer is getting older and older, thus increasing the user's perceived latency.
In triple-buffer systems, we create ourselves three buffers -- one front buffer and two back buffers. The idea is this:
The monitor is displaying the front buffer, and we're drawing into back buffer #1. If we finish drawing into back buffer #1 before the monitor finishes drawing the front buffer, then instead of waiting for vsync, we instead immediately start drawing the next frame into back buffer #2. If we finish and vsync still hasn't come, we start drawing back into back buffer #1, and so on. The idea is that when vsync eventually happens, one or the other of our back buffers will be complete, and that one can be swapped for the front buffer.
The benefit of triple-buffering is that we don't lose the time that we spent waiting for vsync in the double-buffering approach, and the image swapped onto the front buffer may be "fresher" than one which had been waiting for vsync for 8ms. The down-side of triple-buffering is that we need extra memory for storing the extra screen image, and that our CPU/GPU usage will be higher (again, since we don't slow down to wait for vsync).
Typically, modern drivers will often perform triple-buffering transparently, behind the scenes. You write your code to do double-buffering, and the driver will actually return control to you early, and just internally handle swapping between however many back buffers it wants to use, without your code ever being aware of it.
GPU vendors currently recommend that you not implement triple-buffering yourself -- the driver will do it for you automatically.