luni, 15 februarie 2010

Java: Serializare

//Clasa care urmeaza a fi serializata

import java.util.*;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.*;

public class ClasaSpreSerializare implements Serializable{
private int campPrivat;
protected int campProtejat;
public int campPublic;
private static int campStatic;
protected transient int campTransient;
public ClasaSpreSerializare(){}
public ClasaSpreSerializare(int x,int y,int z, int a, int b){
campPrivat=x;
campProtejat=y;
campPublic=z;
campStatic=a;
campTransient=b;
}
public String toString(){
return "campPrivat: "+campPrivat+" campProtejat: "+campProtejat+" campPublic: "+campPublic+" campStatic: "+campStatic+" campTransient: "+campTransient;
}
public void writeObject(ObjectOutputStream obj)throws Exception{
obj.writeInt(campProtejat);
}
public void readObject(ObjectInputStream obj2)throws Exception{
campTransient=obj2.readInt();
}
}


//Clasa de test

import java.util.*;
import java.io.*;

public class TestClasa {
public static void main(String[] args){
ClasaSpreSerializare test1=new ClasaSpreSerializare(1,2,3,4,5);
System.out.println("Continutul obiectului inainte de serializare: "+test1.toString());

try{
ObjectOutputStream obj=new ObjectOutputStream(new FileOutputStream("fis.txt"));
obj.writeObject(test1);
test1.writeObject(obj);
obj.close();
}catch(Exception ex){
System.out.println("Eroare la scriere!!!");
}

try{
ObjectInputStream obj=new ObjectInputStream(new FileInputStream("fis.txt"));
ClasaSpreSerializare test2=(ClasaSpreSerializare)obj.readObject();
test2.readObject(obj);
System.out.println("Obiectul dupa deserializare: "+test2.toString());
}catch(Exception ex){
System.out.println("Eroare la citire");
}
}
}


/**Metode de serializare:
1. Extinderea unei clasa serializabile
2. Implementare Serializable
3. Metode de serializare obisnuite(vezi writeObject(ObjectOutputStream obj) din exemplu; (se poate folosi pt transmiterea unor campuri, de ex transient)
4. Implementare Externalizable(tot o interfata marker), asemanator cu metoda descrisa mai sus la 3., folosesc flux pe Object
5. Declarare interfata si metode aferente(cel mai general), metodele folosesc fluxuri DataOutputStream
Vezi: Java de la 0 la expert
*/

Java: Problema producator - consumator

//Clasa Producator


public class Producator extends Thread {
private Produs copieProdus;
public Producator(Produs produs){ //constructor
copieProdus=produs;
}
public void run(){
for(int i=0;i<10;i++){ //incearca sa "creeeze" 10 produse
copieProdus.set(i);
try{
sleep(500);
}
catch(Exception ex){
System.out.println("Exceptie producator");
}
}
}
}

//Clasa Consumator


public class Consumator extends Thread{
private Produs copieProdus;
public Consumator(Produs produs){
copieProdus=produs;
}
public void run(){
for(int i=0;i<10;i++){
copieProdus.get(); //incearca sa "consume" 10 produse
try{
sleep(500);
}
catch(Exception ex){
System.out.println("Exceptie Consumator");
}
}
}
}


//Clasa produs


public class Produs {
private boolean available=false;
private int container=-1;
public synchronized void set(int valoare){
while(available==true){
try{
wait(5000); //blocheaza firul solicitant daca avem deja creat un obiect
//Thread.sleep(50);
}
catch(Exception ex){
System.out.println("Eroare set");
}
}
available=true;//pentru ca, consumatorul sa poata consuma
container=valoare;
System.out.println("Setare produs: "+container);
notifyAll();//deblocheaza toate firele, in cazul de fata ofera o sansa consumatorului
}
public synchronized int get(){
while(available==false){
try{
wait(5000);//blocheaza firul solicitant daca nu avem creat un obiect
//Thread.sleep(50);
}
catch(Exception ex){
System.out.println("Eroare get");
}
}
available=false;//pentru ca, producatorul sa poata sa produca
System.out.println("Consumare produs: "+container);
notifyAll();//deblocheaza toate firele, in cazul de fata ofera o sansa producatorului
return container;
}
}

//Clasa Main


public class Main {
public static void main(String[] args){
Produs produs=new Produs();
Consumator consumator=new Consumator(produs);
Producator producator=new Producator(produs);
producator.start();
consumator.start();
}
}



/**Modul de functionare:
* In clasa Main se face o instanta a Produsului, si este trimisa ca si parametru Consumatorului si Producatorului. Se pornesc metodele start ale Threadurilor
* Atat Producatorul cat si Consumatorul incearca sa faca de 10 ori operatia de set/get din Produs.
* Daca nu se pot face get/set din Produs, prin folosirea lui wait() se va bloca Consumatorul/Producatorul, blocarea putandu-se realiza doar unei categorii
* Un ex. posibil de functionare: Consumatorul obtine cheia Produsului si incearca accesarea metodei get(); daca avem o valoare in Produs, atunci citirea se face cu succes(nu intra pe while) si se deblocheaza Producatorul; daca nu avem valoare, se intra pe while si se blocheaza Consumatorul
* Producatorul obtine cheia Produsului incearca sa faca o inscriere, daca nu avem valoare in Produs se va putea si se deblocheaza Consumatorul, daca vem deja o valoare se va bloca Producatorul
* OBS: Metodele wait() si notifyAll() se pot folosi in metodele sincronizate mult mai eficient decat Thread.sleep(). Un obiect blocat cu wait se va debloca dupa trecerea timpului sau prin apelare notify, notifyAll. Se evita astfel blocajele(decomenteaza sleep si comenteaza notifyAll si wait in acest ex).
* Fie 3 fire:1,2,3; daca firul 3 se poate executa doar dupa terminarea activitatii firelor 1 si 2 putem uni cele 2 fire prin join ex: fir2.join();
* Se pot de asemenea folosi si 2 Producatori si 2 Consumatori */

Vezi: Thread si Introducere in universul Java, Horia Georgescu, Ed. Tehnica 2002.

vineri, 12 februarie 2010

Java: Ceas digital (folosite Thread, notify,wait)

//Clasa Secunde


public class Secunde extends Thread {
private Tic copieTic;
private int secunde;
public Secunde(Tic tic)
{
copieTic=tic;
}
public void run()
{
//daca nu folosim o bucla infinita dupa 1 secunda se termina ciclul si threadul "moare"
while(true)
{
try{
Thread.sleep(1000);
}
catch(Exception e)
{
System.out.println("Eroare in Secunde");
}
System.out.println("Sec: "+secunde);
if(secunde==59){
secunde=0;
copieTic.increment();
}
else
secunde++;
//se face o incrementare la fiecare 1000ms,
//daca s-a ajuns la 59, se revine in 0 si se semnalizeaza in Tic
}
}
}


//Clasa Minute


public class Minute extends Thread{
private Tic copieTic;
private int minute;
public Minute(Tic tic){
copieTic=tic;
}
public void run(){
while(true){
copieTic.astept();
minute++;
System.out.print("Minute: "+minute+" ");
if(minute==59)
minute=0;//in mod normal s-ar incrementa orele...
}
}
}


//Clasa Tic, realizeaza comunicarea intre fire


public class Tic {
private boolean eveniment;
public synchronized void astept(){
while(!eveniment)
{
try{
wait();
}
catch(Exception ex){
System.out.println("Eroare in astept()");
}
}
eveniment=false;
}
public synchronized void increment(){
System.out.println("In increment");
eveniment=true;
notify();
}
}


//Clasa Main, principala


public class Main {
public static void main(String[] args){
Tic tic=new Tic();
Secunde sec=new Secunde(tic);
Minute min=new Minute(tic);
sec.start();
min.start();
}
}



/**Scurte precizari
* Folosirea lui wait() are ca rol de a trece obiectul apelant in starea Blocked
* Folosirea lui notify() are efectul invers
* Metodele de tip synchronized produc un "lacat" per clasa si nu per obiect
* Variabila booleana eveniment se foloseste pentru verificare(se poate renunta la folosirea ei)
* Se poate modifica ai sa nu mai folosim wait() si notify prin testarea directa in Minute daca var booleana eveniment a devenit true
*/
Functionare: Se pornesc cele 2 fire, "lacatul" de pe clasa Tic este folosit de catre Minute(prin metodaastept()), care are ca efect blocarea lui Minute. Intre timp, threadul Secunde continua incrementarea secundelor. Cat timp nu s-a ajuns la 59, nu se apeleaza metode din Tic, deci implicit nu folosim lacatul. Cand se ajunge la 59 secunde, prin metoda increment() din Tic se deblocheaza threadul Minute. El va apela astept() se blocheaza, si ciclul se reia...

Vezi Head First in Java, in special paginile 490 - 516 si Unraveling threads sau Sincronizarea threadurilor(exemplul producator-consumator)

joi, 11 februarie 2010

JAVA: Thread + Animation

//Clasa Desenare: deseneaza un oval la coordonatele x,y si un fundal

import javax.swing.*;
import java.awt.*;
public class Desenare extends JPanel{
public int x,y,diametru=40;
public void paintComponent(Graphics g)
{
g.setColor(Color.CYAN);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.yellow);
g.fillOval(x, y, diametru, diametru);
}
}

//Clasa Fir: creaza un JFrame, si un Thread, adauga ascultatori


//import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.*;
public class Fir implements Runnable
{
Thread unFir;
public Fir()
{
frame.getContentPane().add(oval);
frame.setSize(500,400);
frame.setVisible(true);
frame.addMouseListener(new List());
}
JFrame frame=new JFrame();
Desenare oval=new Desenare();
public void run()
{
for(int i=0;i<(20);i++)
{
try{
Thread.sleep(500);
}
catch(Exception ex)
{
System.out.println("Exceptie");
}
oval.x+=i;
oval.y+=i;
oval.repaint();
}
//dupa ce s-a terminat "repictarea" trebuie "omorat"
//threadul pentru a se putea incepe unul nou
unFir=null;
System.out.println("Thread distrus");
}
public void refresh()
{
if(unFir==null)
{
unFir=new Thread(this);
unFir.start();
}
}
class List implements MouseListener{
public void mousePressed(MouseEvent e) {};
public void mouseReleased(MouseEvent e) {};
public void mouseClicked(MouseEvent e)
{
System.out.println("Noile coordonate: "+e.getX()+" "+e.getY());
oval.x=e.getX();
oval.y=e.getY();

refresh();

}
public void mouseEntered(MouseEvent e) {};
public void mouseExited(MouseEvent e) {};
}
}

//Clasa Main, creaza un obiect de tip Fir


public class Main {
public static void main(String[] args)
{
Fir fir=new Fir();
}
}



/**Scurte indicatii:
* Daca nu se verifica sa avem un singur fir in metoda refresh prin: unFir==null se vor crea fire la fiecare eveniment mouse.
* De asemenea este important ca threadul sa devina null, pentru a se putea reincepe desenarea in cazul in care exista
un nou eveniment mouse
* Daca nu se foloseste verificarea unFir==null atunci se vor porni in pararel mai multe fire, si chiar daca se pastreaza unFir=null acesta nu "omoara" firul, doar pierdem referinta la fir, el "murind" doar dupa ce se termina metoda run(in cazul de fata dupa 10 secunde)
* Chiar daca in timpul "desenarii" se va da click in alta zona, folosind versiunea prezentata mai sus a prog el se va executa tot in cele 10 secunde alocate, se vor schimba doar coordonatele de desenare
*/
Observatii: Se poate folosi si pentru deplasare pe o harta, prin stabilirea celei mai scurte rute de la pozitia initiala pana la noile coordonate; sper ca in scurt timp sa postez si versiunea respectiva, inca in lucru;))