NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
IT rekvalifikace s podporou uplatnění. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!

Diskuze: Posloupnost vláken

V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:25.4.2016 12:29

Zdravím všechny, potřeboval bych poradit ne zcela se zásadním problémem, protože, co chci, to mi funguje. Tento ticket píšu jen pro informaci, abych věděl, jak a jestli to funguje.

O co jde: dělám window aplication, do které chci implementovat efekt v podobě vysouvacího a zasouvacího okna (okno se prostě zvětší a zmenší). Jelikož toto okno chci používat v několika JDialozích, tak by byla blbost dělat ten samý efekt pro každý JDialog zvlášť. Proto jsem se rozhodl pro singletona a jak jsem řekl v úvodu, tak funguje jak má. funguje tak, že tlačítko vyvolá efekt zvětšení a to samé tlačítko nebo další efekt zmenšení. Díky singletonu mám krásně ošetřen i stav, ve kterém se okno zrovna nachází a su na to náležitě hrdý:D

Kód vypadá následovně (za ty české názvy proměnných se omlouvám, už mi to otřískal ohlavu i kolega v JavaScriptech.D):
Main class, která nepotřebuje žádný komentář

import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Main {
//###################-ATRIBUTY-###################
//==========KONSTANTNI ATRIBUTY TRIDY==========
//===========PROMENNE ATRIBUTY TRIDY===========
//========KONSTANTNI ATRIBUTY INSTANCI=========
//=========PROMENNE ATRIBUTY INSTANCI==========

    private final JFrame okno;
    private final JButton tlacitko;
//####################-STATIC-####################
//============STATICKY KONSTRUKTOR=============
//=========STATICKE PRISTUPOVE METODY==========
//==============STATICKE METODY================
//=================MAIN METODA=================

    public static void main(String[] args) {
        Main main = new Main();
    }
//###################-INSTANCE-###################
//=================KONSTRUKTOR=================
//=============PRISTUPOVE METODY===============
//===================METODY====================
//###############-SOUKROME METODY-################
//=====STATICKE SOUKROME A POMOCNE METODY======
//==========SOUKROME A POMOCNE METODY==========
//############-SOUKROME KONSTRUKTORY-#############
//===============SOUKROME TRIDY================
//==============TESTOVACI METODY===============

    public Main() {
        okno = new JFrame();
        okno.setSize(100, 100);
        okno.setVisible(true);
        okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        tlacitko = new JButton();
        tlacitko.setSize(50, 50);
        okno.add(tlacitko);
        tlacitko.addActionListener((ActionEvent evt) -> {
            Efekt.getEfekt().start();
        });
    }
}

Efekt class, která vytváří okno, tlačítkem ho zvětšího a tím samým tlačítkem zmenší

import java.awt.Color;
import javax.swing.JWindow;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JPanel;

public class Efekt implements Runnable {
//###################-ATRIBUTY-###################
//==========KONSTANTNI ATRIBUTY TRIDY==========
//===========PROMENNE ATRIBUTY TRIDY==========

    private static Efekt efekt;
//========KONSTANTNI ATRIBUTY INSTANCI=========

    private final Toolkit tool = Toolkit.getDefaultToolkit();
    private final Dimension src = tool.getScreenSize();
    private final JWindow okno;
    private final int sirka = src.width;
    private final int vyska = src.height;
    private final double pomer = (double) vyska / (double) sirka;
    private final JPanel panel;
//=========PROMENNE ATRIBUTY INSTANCI==========

    private Thread vlakno;
    private boolean zobraz = true;
//####################-STATIC-####################
//============STATICKY KONSTRUKTOR=============
//=========STATICKE PRISTUPOVE METODY==========

    /**
     * Metoda getEfekt slouží pro vrácení singletona. Pokud ještě neexistuje,
     * tak si jej vytvoří.
     *
     * @return Efekt
     */
    public static Efekt getEfekt() {
        if (efekt == null) {
            efekt = new Efekt();
        }
        return efekt;
    }
//==============STATICKE METODY================
//=================MAIN METODA=================
//
//    public static void main(String[] args) {
//        new A();
//    }
//###################-INSTANCE-###################
//=================KONSTRUKTOR=================

    /**
     * Soukromou třídou zajišťuji, že se nedá vytvořit nová instance této třídy.
     * Okdaz na jedinou instanci třídy Efekt vrací metoda getEfekt()
     */
    private Efekt() {
        okno = new JWindow();   // Chci, aby okno efektu nemělo lištu, proto
        // pracuji s JWindow
        okno.setSize(5, 5);
        panel = new JPanel();   // Nový panel vytvářím kvůli barvě pozadí
        panel.setSize(okno.getSize());
        panel.setBackground(new Color(84, 112, 251));
        okno.add(panel);
        okno.setVisible(true);
    }
//=============PRISTUPOVE METODY===============
//===================METODY====================

    public void start() {
        if (vlakno == null || !vlakno.isAlive()) {
            vlakno = new Thread(this);
            vlakno.start();
        }
    }

    @Override
    public void run() {
        int prodleva = 2;   // prodleva představuje milisekundovou pauzu pro
        //"plynulé" překreslování okna
        if (zobraz) {
            for (int i = 5; i <= sirka - 300; i += 5) {
                okno.setSize(i, (int) (i * pomer));
                okno.setLocation(sirka / 2 - okno.getWidth() / 2, vyska / 2 - okno.getHeight() / 2);
                wait(prodleva);
            }
        } else {
            for (int i = sirka - 300; i >= 5; i -= 5) {
                okno.setSize(i, (int) (i * pomer));
                okno.setLocation(sirka / 2 - okno.getWidth() / 2, vyska / 2 - okno.getHeight() / 2);
                wait(prodleva);
            }
        }
        zobraz ^= true;
    }
//###############-SOUKROME METODY-################
//=====STATICKE SOUKROME A POMOCNE METODY======
//==========SOUKROME A POMOCNE METODY==========

    /**
     * Pomocnou metodu wait jsem si vytvořil, protože su líný psát pořád
     * vyjímky:D
     *
     * @param milisekund //doba v milisekundách, po kterou má být pauza
     */
    private void wait(int milisekund) {
        try {
            Thread.sleep(milisekund);
        } catch (InterruptedException exp) {
        }
    }
//############-SOUKROME KONSTRUKTORY-#############
//===============SOUKROME TRIDY================
//==============TESTOVACI METODY===============
}

Tak, toto mi funguje a pro mé účely je to v pořádku. No a tento ticket píšu proto, jestli by mi někdo neporadil, jak to udělat v jednom vláknu. Tím myslím, aby se zavolala třeba nová metoda metoda(), která by spustila dvakrát vlákno Efekt.getEfek­t().start();. První by se provedlo první vlákno (druhé čekalo, až to druhé dokončí svou činnost) a pak až se spustí to druhé vlákno. Hledal jsem týden po netu řešení. Prošel si nespočet toturiálů a jediné, co trošku fungovalo podle mých představ, tak byl tutoriál, ve kterém se používala metoda join(): v další ukázce kódu ukážu ActionEvent z konstruktoru třídy main:

public Main() {
        okno = new JFrame();
        okno.setSize(100, 100);
        okno.setVisible(true);
        okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        tlacitko = new JButton();
        tlacitko.setSize(50, 50);
        okno.add(tlacitko);
        tlacitko.addActionListener((ActionEvent evt) -> {
            Thread t = new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        System.out.println(i + 1);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            };
            t.start();
            try {
                t.join();
            } catch (InterruptedException ex) {
            }
            Efekt.getEfekt().start();
        });
    }

Tento kousek kódu taky funguje, ale když jsem rúzně experimentoval s přidáním dalším kouskem kódu (Efekt.getEfek­t().start();), tak mi to nefungovalo jak mělo.

Při experimentech jsem zkoušel synchronizované bloky, metody wait(), join(), i různé fígle, co jsem našel a nic nepomohlo. Poradil by někdo, jak to napsat (třeba i jinak), ale aby to fungovalo?

Odpovědět
25.4.2016 12:29
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Jindřich Máca
Tvůrce
Avatar
Odpovídá na Lubor Pešek
Jindřich Máca:26.4.2016 11:57

Ahoj, v první řadě Tě musím pochválit za dobré popsání problému a poslání okomentovaných zdrojových kódů. Když si to člověk přečte, hned je mu jasné, o co jde. :-)

Bohužel hned za druhé musím konstatovat, že to, co popisuješ, zdrojový kód nedělá nebo minimálně u mě to nefunguje. Zkopíroval jsem si ho a spustil a probíhá to přibližně následovně:

  • První část je v pořádku, při kliku na tlačítko se začne s pěkným efektem zvětšovat okno.
  • Když se ale zvětší, je chvíli prodleva a pak najednou přeskočí na mnohem větší velikost. Prostě se z ničeho nic instantně zvětší.
  • No a když znovu kliknu na tlačítko, okno zmizí bez žádného efektu.

Takže nejdřív byl začal asi tím, že tohle pořádně odladíš.

Potom k tvému dotazu a k těm vláknům. Tady to přeci nepotřebuješ za každou cenu dělat více vlákny. Vlákna jsou z principu navržena tak, aby běžela paralelně a synchronizovat je pro běh po sobě nedává moc smysl...

Co takhle tam mít např. synchronizované počítadlo? Jednoduše pokud je na počítadle 0, nastartuješ efekt, jinak tam přičteš 1. Jakmile efekt doběhne, sníží počítadlo o 1 a pokud není 0, spustí se efekt znovu. Je to úplně primitivní řešení, které mě teď napadlo, třeba sám přijdeš na lepší, pokud se nad tím zamyslíš tímto způsobem. ;)

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
26.4.2016 11:57
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:26.4.2016 12:10

Opravdu ti to dělá tento problém? já jsem si to ještě jednou zkopíroval přímo z toho, co jsem sem dal a funguje jak má:O V těch dvou původních třídách žádná prodleva ani není, tak nevím. (zkoušel jsem to v eclipse, netbeansech i blueJ a jede to:)

Jediná chyba, co mě napadá, jak by to mohlo (ne)fungova, jak říkáš, tak žes třeba špatně nakopčil ten XOR command nebo ti tvoje IDE XOR nepodporuje.

Jinak jsem to psal, že to nepotřebuji k tomuto projektu, ale abych se to naučil do budoucna:) Já se nikdy nespokojeím, že něco nějak funguje. Když si něco zkouším, učím se něco nového, tak si udělám další 4 examply, na kterých si experimentuju. A posloupnost vláken se dá krásně využít při načítání - nejdřív jedním vláknem načteš, druhým zpracuješ a třetí potom začne data používat

Nahoru Odpovědět
26.4.2016 12:10
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.