Update: There is a newer version of this page on the singleton anti-pattern on my new website; however, the new article does not touch upon global variables or environment variables, and thus I will keep up this old page for the time being.
This advice should be obvious. All three of these involve shared, mutable state. Whenever shared mutable state is involved, it is easy for components to step on each other's toes.
This article, entitled "Global Variables are Bad", spells out plenty of reasons why you should avoid using global variables. I won't repeat all of the reasons here -- you should read the linked article --, but just to reiterate some of the most important (or what I found to be the most important) reasons to avoid global variables:
Environment variables are overused and abused for configuration purposes. Using the default native preferences/settings/configuration mechanism (registry on Windows, PLIST files in ~/Library/Preferences or NSUserDefaults on Mac OS X, and GConf on Linux) or a set of configuration files is a better and more user-friendly way of configuring software. For C++ programmers, the Qt Framework provides a QSettings class which makes it easy to load/store preference data in the default preferred native mechanism but in a cross-platform manner. For Java programmers, the Preferences class provides equivalent functionality, making it easy to load/store configuration data without resorting to environment variables.
Dependency Injection Myth, the Root Cause of Singletons, Singletons are Pathological Liars, and Why Singletons are Evil all give excellent reasons to avoid the Singleton anti-pattern (yes, it is an anti-pattern). The solution to the singleton anti-pattern is to use dependency injection.
Dependency injection can be done without a dependency injection framework, simply by making your singletons instantiable like normal classes (eliminate the private constructor, the static singleton instance variable, and the singleton static construction function), instantiating only one instance of that class in your main function, and then passing it to whatever objects need it using some interface that your singleton class implements. Although dependency injection is completely doable without a dependency injection framework, using a dependency injection framework will make your life much simpler and easier, so you might as well go for it. If you are coding in Java, then Guice is definitely the way to go.
If you'd like to know more about Google Guice, you may be interested in watching the following two videos about the Google Guice dependency injection framework. Even if you are not interested in using the Google Guice framework, you may be interested in watching these videos as they explain why singletons (with private constructors and static construction methods) are bad, as well as what dependency injection is, and how the use of dependency injection solves the problem of using the singleton anti-pattern.
Software Engineering >