Render stuttering

I have been searching the whole internet for a working fix, it helped with most of the stuttering, but every now and then I still feel it is not smooth. I’m pretty new to actual game programming in Java, and I really hope anyone can help me with this problem.

The code I have is:

package com.game;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class Main implements Runnable {
	
	public final static int GAME_WIDTH = 640, GAME_HEIGHT = 480;
	
	BufferedImage tile;
	//BufferedImage tile = new BufferedImage(48, 48, BufferedImage.TYPE_INT_RGB);
	
	int[] x = new int[1000];
	int[] y = new int[1000];
	
	final int SIZE = 48;
	
	Canvas canvas;
	
	public void load() {
		try {
			tile = ImageIO.read(new File("./grass.png"));
		} catch(IOException e) {
			
		}
		
		int width = 0, height = 0;
		for(int i = 0; i < x.length; i++) {
			x[i] = width*SIZE;
			y[i] = height*SIZE;
			width++;
			if(width > 80) {
				width = 0;
				height++;
			}
		}
	}
	
	public Main() {
		load();
		
		canvas = new Canvas();
		
		JFrame frame = new JFrame("Game");
		frame.setResizable(false);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		frame.setSize(GAME_WIDTH, GAME_HEIGHT);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		
		frame.add(canvas);
		

		(new Thread(this)).start();
		
	}
	
	public static void main(String[] args) {
		new Main();
	}
	
	int offsetX = -200;
	
	public void tick() {
		int speed = -3;
		
		offsetX += speed;
	}
	
	public BufferedImage image = new BufferedImage(GAME_WIDTH, GAME_HEIGHT, BufferedImage.TYPE_INT_RGB);
	
	public void render() {
		BufferStrategy bs = canvas.getBufferStrategy();
		
		if(bs == null) {
			canvas.createBufferStrategy(3);
			return;
		}
		
		
		Graphics g = bs.getDrawGraphics();
		
		for(int i = 0; i < x.length; i++) {
			
			if(x[i]+offsetX > GAME_WIDTH || y[i] > GAME_HEIGHT)
				continue;
			
			if(x[i]+offsetX + SIZE < 0 || y[i] + SIZE < 0)
				continue;
			
			image.getGraphics().drawImage(tile, x[i]+offsetX, y[i], null);
		}
		
		g.drawImage(image, 0, 0, GAME_WIDTH, GAME_HEIGHT, null);
		image.getGraphics().dispose();
		g.dispose();
		bs.show();
	}
	
	@Override
	public void run() {

		long lastTime = System.nanoTime();
		double nsPerTick = 1000000000/60D;

		int ticks = 0;
		int frames = 0;

		long lastTimer = System.currentTimeMillis();
		double delta = 0;

		while(true) {
			long now = System.nanoTime();
			delta += (now - lastTime)/nsPerTick;
			lastTime = now;

			while(delta >= 1) {
				ticks++;
				tick();
				delta -=1;
			}
			try {
				Thread.sleep(2);
			} 
			catch (InterruptedException e) {

				e.printStackTrace();
			}
			
			frames++;
			render();

			if (System.currentTimeMillis() - lastTimer >= 1000) {
				lastTimer += 1000;
				System.out.println(""+ticks+ " ticks, "+frames+ " frames");
				frames = 0;
				ticks = 0;
			}
		}
	}

}

Thanks in advance.