How do two perceptrons produce different linear decision boundaries?
In the article which you linked, it seems that none of the two perceptrons are trained on data, you just have two perceptrons with different weights w0 and w1. If they have different weights, then they will have two different decision boundaries. 
You can see that, looking at the code that plots the decision boundaries:

ax.plot(t0, decision_boundary(w0, t0), 'm', label='Perceptron #0 decision boundary')
ax.plot(t1, decision_boundary(w1, t1), 'g', label='Perceptron #1 decision boundary')


where decision boundaries is 

def decision_boundary(weights, x0):
    return -1. * weights[0]/weights[1] * x0