Hamlin passed Kevin Harvick for the lead with 29 laps to go Sunday and earned his third consecutive NASCAR Sprint Cup win at the smallest circuit in the series.
"I don't think I've ever closed that well, ever," Hamlin said after his 15th career victory, his series-best seventh this year and his fourth at Martinsville.
He didn't view the race as a must-win, but rather "must finish in front of," he said.
And the victory came after a miserable start. Determined to out-qualify Johnson and get the sometimes critical first pit stall, Hamlin won the pole for just the second time this season. Johnson qualified 19th, and Harvick 36th.
But once the race began, Hamlin went backward. By the first caution and restart about 50 laps in, he was behind both of them. Johnson was eighth, Harvick ninth and Hamlin 12th.
"I was very worried," he said. "Everyone saw how far back we were dropping and I thought it was the end of our day. We kept fighting, kept digging, kept making up spots."
Hamlin became the first driver since Johnson in 2007 to sweep both races here, and he and Harvick closed the gap on Johnson in the points race with four events to go. Hamlin cut his 41-point deficit to six, the smallest with four races left since the Chase began in 2004.
"Who said this was over?" Hamlin asked after the race. "I told you it wasn't over."
Harvick finished third after Mark Martin passed him late, but Harvick had his best showing in 19 career starts at the 0.526-mile oval; he'd never been better than seventh.
"We kept the 48 behind us and the 11 in sight in what everybody said was a two-horse race, and we were right there in the middle of it," Harvick said. He gained 15 points in the standings and now trails Johnson by just 62 heading to Talladega, where he won in the spring.
"It's a lot of fun right now," Harvick said.
Johnson, the four-time defending series champion, rallied to finish fifth.
"For awhile I thought the 29, the 11 and us were just going to ride together all day long," Johnson said of Hamlin and Harvick, "but then the 11 got going there at the end."
Harvick led during most of a long green-flag run until Hamlin got to his rear bumper, then went to his inside. The two raced side-by-side for about five laps before Harvick checked up coming out of Turn 2, finally falling in line behind Hamlin on lap 472.
The race had 15 cautions for 90 laps, but the final 98 were run under a green flag.
"That's exactly what we needed at the end," Hamlin's crew chief, Mike Ford, said.
The race seemed likely to be a battle between Johnson and Hamlin, who had combined to win the last eight races at Martinsville and began the day 1-2 in the point standings.
Instead of domination, 12 different drivers led and there were 24 lead changes.
Jeff Burton led the most laps with 134, but faded at the finish and came in ninth.
Jeff Gordon had a rough day, seriously damaging his chance for a fifth championship. He entered fourth in points, 156 behind Hendrick Motorsports teammate Johnson, but was spun into the frontstretch wall by Kurt Busch after running in contention all race long.
Gordon wound up 20th and fell to fifth in the standings, 203 points behind Johnson.
Kyle Busch finished fourth and moved up to fourth in the standings, 172 back.