Cześć, zadałeś bardzo dobre pytanie, na które już Ci odpowiadam 
Po 1: Większość komponentów Swing nie jest bezpieczna wątkowo i nie można robić czegoś takiego jak:
extends JFrame implements Runnable
Po 2: Używając wątków w połączeniu z biblioteką Swing, trzeba dostosować się do dwóch prostych zasad:
- Jeżeli jakieś działanie jest czasochłonne, należy je wykonać w osobnym wątku roboczym - nigdy w wątku dystrybucji zdarzeń.
- Nie należy operować na komponentach Swing w żadnym innym wątku niż wątek dystrybucji zdarzeń.
Teraz przeanalizujmy to na przykładzie, który napisałem na szybko:
import java.awt.EventQueue;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new KlasaTestowa();
}
});
}
}
EventQueue to wątek dystrybucji zdarzeń, o którym wspomniałem wyżej. W nim tworzymy i operujemy elementami Swinga. Powyżej tworzę po prostu główne okno programu (JFrame) - KlasaTestowa.
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
public class KlasaTestowa extends JFrame {
public static final int MAX = 10;
private JLabel labelAlfa;
private JButton buttonAlfa;
public KlasaTestowa() {
labelAlfa = new JLabel("Test");
buttonAlfa = new JButton("Wielowątkowość");
setLayout(new GridLayout(2, 1));
add(labelAlfa);
add(buttonAlfa);
buttonAlfa.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Thread(new Odliczanie(MAX, labelAlfa)).start();
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
Powyżej definiuję główne okno programu. Zwróc uwagę na dodany ActionListener do przycisku buttonAlfa. Tworzymy tutaj nowy wątek (zasada 1), który będzie odliczał do 0 i aktualizował wartość labelAlfa. Oczywiście od razu go uruchamiamy metodą start(). Odliczanie jest czasochłonne, bo trwa przez dany okres czasu 
import java.awt.EventQueue;
import javax.swing.JLabel;
public class Odliczanie implements Runnable {
private static final int DELAY = 1000;
private int max;
private JLabel labelAlfa;
public Odliczanie(int max, JLabel labelAlfa) {
this.max = max;
this.labelAlfa = labelAlfa;
}
@Override
public void run() {
try {
while(max > 0) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
labelAlfa.setText(String.valueOf(max));
}
});
max = max - 1;
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Naszym celem jest zmienić napis w labelAlfa (przekazany w konstruktorze wątku - robimy to przez referencję). Aby go osiągnąć, w metodzie run() klasy Odliczanie wykonujemy ową operację wewnątrz wątku dystrybucji zdarzeń EventQueue (zasada 2).