You should ask this type of question on https://gamedev.stackexchange.com/
But to answer your questions anyway, there are a LOT of ways you can structure it. For a small game, you'd likely be better off using some sort of basic OOP structure that tends toward composition over inheritance. (wikipedia entry, and an SO question on this topic). That should give you some amount of flexibility and reusability and help ward off spaghetti.
Typically you want your frame updates to occur on the main thread. No need to completely obliterate determinism in a coding project that's already going to have elements of randomness to deal with! Although it is not unheard of to execute object updates across multiple threads, this is something you may want to hold off on until you absolutely need it. Avoid premature optimization.
You'll want to look into timing mechanisms and managing FPS & game clock. There are accessible, simple techniques out there that don't require multi-threaded programming.
Another thing to think about is sometimes the game experience doesn't become more enjoyable just because your programming concepts are fancy. You might find that players get frustrated when animation and FPS slow downs don't sync up nicely with movement.
There are so many variables relating to efficiency of your game that it's hard to answer this - but these days I would less concerned about slow machines than I would be about first making a cool game with fun mechanics and cool enough graphics and shaders that it's visually stimulating without feeling like I need to compete with John Carmack.
Secondly, when I call the draw function of, for example, my 'bird' class, the function needs to know which frame of the animation it's up to, and return the appropriate image.
This raises red flags to me. I may be assuming the worst, but rather than glossing over it let's talk about it. In a "game" with a small number of sprites, you could potentially get away with loading separate images into memory and displaying them as needed. But that can get ugly in a hurry as time goes by in development and you start to add objects to your application. For every image you want to display, the hardware (the thing that implements the whatever GL specification you're targeting) will need to reserve some memory for these images. Not a horrible thing in and of itself. The sticky, bottlenecky part of this is when the graphics hardware wants to actually draw stuff. It has to take what's sitting in general memory on the hardware and stick it in video memory. Hence, sprite sheets (or texture atlases) become very useful - rather than saying "hey draw this other image" you just say "hey draw a different part of the same big image" (and those different "parts" would be the frames of your animations; multiple animations can live inside a texture atlas; texture atlases are generally sized by powers of 2 which is optimal for the GPU). Side note on powers of 2.
From this SO question:
The holy rule of GPU programming is that less you talk to GPU, the more she loves you.
Of course you'd then need to balance this against managing shaders, but that's something you can look into and experiment with as you get into this.
Another avenue, which actually is like composition on crack, is an Entity-Based Component System. This allows you to add behaviors and properties to objects (like the ability to react to input commands from the controller/keyboard, or the ability to draw to the screen, or the ability to take damage or modify 'health' or be invincible, the ability to react to physics/gravity).
For a small game, I would advise you not to do that, unless you're treating this like a massive learning exercise. But even then, it's probably a good idea to break some of these concepts into bite-sized chunks and approach them one game at a time.
I hope that helps, good luck.