|
|
Kierunek Informatyka
|
|||
|
|
||||
Instrukcja do ćwiczeń laboratoryjnych nr:
|
11
|
Nazwa przedmiotu:
|
||
Temat: Wyjątki (część 1) i animacje
|
Tryb studiów: stacjonarny |
|||
|
Czas
trwanie ćw. 2x45 min |
||||
|
Autor materiałów:
dr Marcin Skuba |
||||
1. Treści programowe:
Wątki, programowanie współbieżne, główny wątek aplikacji, animacje, metody
graficzne.
2. Cel zajęć:
Zrozumienie mechanizmów programowania współbieżnego.
Opanowanie umiejętności wykorzystania wątków do animacji obiektów graficznych.
Praktyczne wykorzystanie narzędzi graficznych w Java2.
3. Materiały
Wątek jest reprezentowany prze obiekt utworzony z klasy Thread.
Konstruktor jako argument pobiera klasę z zaimplementowanym interfejsem Runnable lub instancję tego interfejsu z metodą run.
Wątki (Threads) w Javie są podstawowym mechanizmem pozwalającym na wykonywanie
wielu zadań równolegle (lub współbieżnie) w ramach jednego programu
(procesu).
Podstawowe Informacje o Wątkach
·
Wątek to pojedyncza sekwencja wykonywania wewnątrz
programu. Każdy program Javy, zaraz po uruchomieniu, posiada co najmniej jeden
wątek, zwany głównym wątkiem (main thread).
·
Wielowątkowość (Multithreading): Pozwala to
programiście na wykorzystanie mocy współczesnych procesorów wielordzeniowych,
co znacznie zwiększa wydajność aplikacji, szczególnie przy zadaniach
wymagających długiego oczekiwania (np. operacje wejścia/wyjścia, komunikacja
sieciowa).
·
Tworzenie Wątków: W Javie wątki można tworzyć na dwa główne sposoby:
1.
Implementując interfejs Runnable
i przekazując obiekt do konstruktora klasy Thread.
Jest to preferowany sposób, ponieważ pozwala klasie dziedziczyć z innej klasy.
2.
Dziedzicząc po klasie Thread
i nadpisując metodę run().
·
Metoda run() i
start(): Logika, która ma być wykonywana w nowym wątku, musi
znajdować się w metodzie run(). Aby faktycznie uruchomić nowy,
współbieżny wątek, należy wywołać metodę start() na obiekcie Thread. Wywołanie bezpośrednio run() spowoduje wykonanie
kodu w bieżącym wątku, a nie w nowym.
·
Synchronizacja: Wielowątkowość wiąże się z ryzykiem powstawania warunków
wyścigu (race conditions), gdy wiele
wątków próbuje jednocześnie modyfikować ten sam zasób. Java rozwiązuje ten
problem za pomocą mechanizmów synchronizacji, takich jak słowo kluczowe synchronized (dla metod lub bloków kodu) oraz klasy
z pakietu java.util.concurrent (np. Lock, Semaphore).
thread = new Thread(this);
Interfejs Runnable:
Konstrukcja metody abstrakcyjnej run interfejsu Runnable:
@Override
public void run() {
}
Pętla niekończąca się uruchomiona w nowym wątku przez metodę run
oraz opóźnienie równe jednej sekundzie:
@Override
public void run() {
while (true){
// miejsce na kod wywoływany w oddzielnym wątku cyklicznie co jedną sekundę try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
Aby uruchomić metodę run
czyli nowy wątek należy z obiektu wątku wywołać metodę start.:
thread.start();
Przykład 1
------------------------------------------------------------------------------------------------------------------------------------------
Przykład aplikacji, w
której pojawia się problem polegający na braku możliwości uruchomienia dwóch
niekończących się pętli.
class Animacja extends JFrame{
Animacja(){
super("Animacja");
setBounds(100,100,450,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelAnimacja anim = new PanelAnimacja();
setContentPane(anim);
setVisible(true);
anim.scroll();
}
static public void main (String arg[]){
new Animacja();
while(true){ // Pętla pierwsza
System.out.print("*");
try{
Thread.sleep(1000);
}catch (InterruptedException e){}
} }}
import javax.swing.*;
import java.awt.*;
class PanelAnimacja extends JPanel{
int y=80;
String tekst[] = {" Wątki są częściami programu, które działają niezależnie ",
" od innych zadań realizowanych przez dany program.",
" Taka właściwość jest często określana mianem wielozadaniowości",
" ponieważ program jednocześnie realizuje więcej niż jedno zadanie. "};
Font font;
PanelAnimacja(){
font = new Font("Arial", Font.BOLD, 12);
}
public void scroll (){
while(true){ //Pętla druga
y--;
if (y < -80)
y=80;
repaint();
try{
Thread.sleep(60);
}catch (InterruptedException e){}
} } public void paintComponent(Graphics g){
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(getBackground());
g2D.fillRect(0,0,getSize().width, getSize().height);
g2D.setColor(Color.black);
g2D.setFont(font);
for(int i=0; i<tekst.length; i++)
g2D.drawString(tekst[i], 10, y+(i*20));
}
}

Poniższy przykład rozwiązuje wspomniany
problem przez zastosowanie oddzielnego wątku, w którym uruchomiona została
jedna niekończąca się pętla oraz druga w głównym wątku aplikacji.
import javax.swing.*;
class Animacja extends JFrame{
Animacja(){
super("Animacja");
setBounds(100,100,450,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelAnimacja anim = new PanelAnimacja();
setContentPane(anim);
setVisible(true);
}
static public void main (String arg[]){
new Animacja();
while(true){
System.out.print("*");
try{
Thread.sleep(1000);
}catch (InterruptedException e){}
} }}
import javax.swing.*;
import java.awt.*;
class PanelAnimacja extends JPanel implements Runnable{
int y=80;
String tekst[] = {" Wątki są częściami programu, które działają niezależnie ",
" od innych zadań realizowanych przez dany program.",
" Taka właściwość jest często określana mianem wielozadaniowości",
" ponieważ program jednocześnie realizuje więcej niż jedno zadanie. "};
Font font;
PanelAnimacja(){
font = new Font("Arial", Font.BOLD, 12);
Thread watek = new Thread(this);
watek.start();
}
public void run (){
while(true){
y--;
if (y < -80)
y=80;
repaint();
try{
Thread.sleep(60);
}catch (InterruptedException e){}
} }public void paintComponent(Graphics g){
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(getBackground());
g2D.fillRect(0,0,getSize().width, getSize().height);
g2D.setColor(Color.black);
g2D.setFont(font);
for(int i=0; i<tekst.length; i++)
g2D.drawString(tekst[i], 10, y+(i*20));
}
}
Wykorzystanie czterech wątków aplikacji.
Trzy panele oraz wątek główny aplikacji, w którym rysowane są gwiazdki
import javax.swing.*;
import java.awt.*;
class Animacja extends JFrame{
Animacja(){
super("Animacja");
setBounds(100,100,450,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(3,1));
PanelAnimacja anim1 = new PanelAnimacja(50);
PanelAnimacja anim2 = new PanelAnimacja(120);
PanelAnimacja anim3 = new PanelAnimacja(200);
panel.add(anim1);
panel.add(anim2);
panel.add(anim3);
setContentPane(panel);
setVisible(true);
}
static public void main (String arg[]){
new Animacja();
while(true){
System.out.print("*");
try{
Thread.sleep(1000);
}catch (InterruptedException e){}
} }}
import javax.swing.*;
import java.awt.*;
class PanelAnimacja extends JPanel implements Runnable{
int y=80;
String tekst[] = {" Wątki są częściami programu, które działają niezależnie ",
" od innych zadań realizowanych przez dany program.",
" Taka właściwość jest często określana mianem wielozadaniowości",
" ponieważ program jednocześnie realizuje więcej niż jedno zadanie. "};
Font font;
int czas=0;
PanelAnimacja(int czas){
font = new Font("Arial", Font.BOLD, 12);
this.czas=czas;
Thread watek = new Thread(this);
watek.start();
}
public void run (){
while(true){
y--;
if (y < -80)
y=80;
repaint();
try{
Thread.sleep(czas);
}catch (InterruptedException e){}
} } public void paintComponent(Graphics g){
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(getBackground());
g2D.fillRect(0,0,getSize().width, getSize().height);
g2D.setColor(Color.black);
g2D.setFont(font);
for(int i=0; i<tekst.length; i++)
g2D.drawString(tekst[i], 10, y+(i*20));
}
}

Przykład 2
-------------------------------------------------------------------------------------------
Przykład aplikacji ukazujący
programowanie współbieżne.
import javax.swing.*;
import java.awt.*;
public class Buttons extends JFrame {
final int MAX=25;
Buttons(){
super("Buttons");
setBounds(100,100,500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(5,5));
for(int i=0; i<MAX; i++){
NewButton
b = new NewButton();
panel.add(b);
}
setContentPane(panel);
setVisible(true);
}
public static void main(String[] args)
{
new Buttons();
}
}
Zmodyfikowana klasa przycisku:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class NewButton extends JButton implements Runnable, ActionListener {
Thread thread;
NewButton(){
thread = new Thread(this);
setBackground(Color.blue);
addActionListener(this);
}
@Override
public void run() {
while (true){
if(getBackground()==Color.blue)
setBackground(Color.red);
elsesetBackground(Color.blue);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} } @Override
public void actionPerformed(ActionEvent e) {
if(!thread.isAlive()) // zabezpieczenie przed ponownym uruchomieniem wątku co spowodowałoby błąd.
thread.start();
}
}

Przykład
wykorzystania wątku bez implementacji interfejsu:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class NewButton extends JButton implements ActionListener {
Thread thread;
NewButton(){
setBackground(Color.blue);
addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
if(thread==null) {
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (getBackground() == Color.blue)
setBackground(Color.red);
elsesetBackground(Color.blue);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }});
if (!thread.isAlive())
thread.start();
}
}}
4. Zadania
Zadanie 1
Wynikiem działania programu jest animacja (gra), w której „piłka” odbija
się od brzegów ramki aplikacji oraz do
belki na dole panelu (na ukos).
Belka poruszana jest razem z myszą po osi Y.
Program składa się z dwóch klas:
PanelAnimacji (dziedziczony z JPanel), który posiada oddzielny wątek
animujący piłkę,
Animacja – główna klasa programu.
