If you are serious about your craft, you probably try to improve your skill set regularly. Read on for some ways I found effective, and which could also help you to improve. The sorting order is roughly by increasing effectiveness.
Single developer approaches
For these approaches, you don't need any friends or peers, but only your trustworthy computer.
Reading books: Believe it or not, the standard classics are actually useful. Books like the Mythical Man-Month, the Pragmatic programmer or Code Complete can give you insights which you wouldn't get otherwise. For me, the most important take-away from these books is to see approaches to problems which have worked already and which I might not be familiar with. So if you didn't do it already, go and grab a few of those books.
Reading source code: Reading other people's source code can be also a gold mine, if done right. Done right means you try to solve the problem on your own first, before looking into a working solution. Point in case, if you want to learn how to write a scanline rasterizer, you would try on your own as hard as you can, before you open up the Quake 1 source code to see how to do it properly.
New tools, development methods, languages: Sounds weird at first, but the idea is to try a different method for developing. If you never did test driven development, try it on the next project. If you don't know Scala, write a small application to get an idea what it is. Same goes for new tools or new libraries. The goal here is to expand the horizon and see what problems are meant to be solved by these new things, and how they approach the problem. Often enough, you can reuse some of the ideas in another context, but you have to constantly expand your toolset. Just keep in mind that none of these tools or methods is going to solve all your problems. While I'm totally for test driven development, I don't use it in my research program at the moment due to a large 3rd party library which cannot be put easily into a test harness instead, I rely on excessive logging, and try to verify the results at each step.
Analyzing your code: Every time you finish a project, or at the end of a week, go ahead and review your code on your own. Look out for two things. First of all, is the code crystal clear to you? Do you see things you could have done better? If so, note them somewhere. The second one is look at the bugs your code had or still has. For each bug, think about how you could avoid it next time. For example, in my code I had once a copy-and-paste problem where I copied three lines to update the three components of a vector. Since that day, I always write a loop if it's more than two iterations of something and I didn't run into a similar problem yet.
No matter how hard you try on your own, one day you'll inevitably hit some plateau, and if you are on your own, it's extremely difficult to escape from it. Be afraid of this day, because if the plateau is high enough, you might have difficulties to notice how much further you can improve, and simply stop. Remember the wise Bruce Lee:
If you always put limit on everything you do, physical or anything else, it will spread into your work and into your life. There are no limits. There are only plateaus, and you must not stay there, you must go beyond them.
True words, so let's go beyond this point, by leaning towards our peers.
Code reviews: I cannot stress this enough, but doing code reviews with someone else is probably one of the most effective ways to improve your programming skills. You get other opinions directly on the stuff you are working on. To get the most out of a code review, don't ask for what is good, but rather encourage people to actively criticize you. People tend to be more honest when pointing out problems than when praising the quality of your code, and after all you want to improve that is, get rid of the stuff which is not good.
Pair programming: If you can, drag a fellow to your computer, and do pair programming with him. To get the most out of it, make sure you discuss the problem first, describe how you want to solve it, and if both of you agree that your approach is viable, let someone watch you how you do it. You'll get a real-time code review, plus the other person is actively trying to solve the problem as well, which makes the comments much more valuable.
Mentor programming: Basically, pair programming taking to the extreme, where you have a really senior developer next to you, who constantly tries to challenge you when you write code. In the best case, your mentor will try to get as many possible approaches for a problem out of you, and encourage you to think through each of them guiding you in this process. Unfortunately, most companies value the time of such senior people far too high to let them train other people, ultimately hurting the progress of the whole development team.