3 Posts in Loop, Show Stickies First

Give this a try. It replaces query_posts(), which you should never use (it kills unicorns) with WP_Query.

Basically it first queries your sticky posts and then, if there were less than your required 3 posts, it will perform another query for the relavent number of posts.

/** Grab the sticky post ID's */
$sticky = get_option('sticky_posts');

/** Query the sticky posts */
$args = array(
    'post__in'          => $sticky,
    'posts_per_page'    => 3,
    'post_type'         => 'post'
);
$sq = new WP_Query($args);

/** Count the number of post returned by this query */
$sq_count = $sq->post_count;

/** Output your sticky posts */
get_template_part('loop', 'query-sticky'); // You'll need to globalize `$sq` in this template

/** Check to see if any non-sticky posts need to be output */
if($sq_count < 3) :

    $num_posts = 3 - $sq_count;
    
    /** Query the non-sticky posts */
    $sticky = get_option('sticky_posts');
    $args = array(
        'post__not_in'      => $sticky,
        'posts_per_page'    => $num_posts,
        'post_type'         => 'post'
    );
    $nq = new WP_Query($args);
    
    /** Output your non-sticky posts */
    get_template_part('loop', 'query-sticky-none'); // You'll need to globalize `$nq` in this template
    
endif;

“I don’t want to globalize in my templates”

If you don’t want to have to globalize the $sq and $nq variables in your templates, rather than get_template_part() you could use locate_template()

locate_template('loop-query-sticky.php');
locate_template('loop-query-sticky-none.php');