domingo, 26 de octubre de 2014

Primer intento de un chat en forma

Como digo arriba, este es el primer chat verdaderamente funcional que he hecho, y a continuación les presento mis clases. Está bastante simple por el momento, con solo 2 clases, pero de que funciona, funciona.

Primero la vista y el socket que envía los mensajes.
package chat;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class chat extends JFrame implements ActionListener, KeyListener{
public chat(int port1, int port2, int pos){
this.port1=port1;
this.port2=port2;
this.pos=pos;
inicializar();
entrada=new recepcion(this,port1);
entrada.start();
}
private Socket cliente;
private ObjectOutputStream flujo_de_salida;
int port1, port2;
int pos;
recepcion entrada;
JTextArea history, message;
JScrollPane sp1, sp2;
JButton send;
JLabel gp;
public void inicializar(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(300*pos,300,300,300);
history=new JTextArea();
sp1=new JScrollPane(history);
sp1.setBounds(10,10,265,180);
history.setEditable(false);
add(sp1);
message=new JTextArea();
sp2=new JScrollPane(message);
sp2.setBounds(10,200,200,50);
message.addKeyListener(this);
add(sp2);
send=new JButton(">>");
send.setBounds(220,200,54,49);
send.setEnabled(false);
send.addActionListener(this);
add(send);
gp=new JLabel();
add(gp);
setVisible(true);
}
public void mensaje_Saliente(String ip, String mensaje){
try{
cliente = new Socket(ip, port2);
flujo_de_salida = new ObjectOutputStream(cliente.getOutputStream());
flujo_de_salida.writeObject(mensaje);
cliente.close();
}
catch(Exception ex){}
}
@Override
public void actionPerformed(ActionEvent ae) {
if(!message.getText().equalsIgnoreCase("")){
mensaje_Saliente("localhost",message.getText());
if(!history.getText().equalsIgnoreCase(""))
history.setText(history.getText()+"\n"+message.getText());
else
history.setText(history.getText()+message.getText());
message.setText("");
send.setEnabled(false);
}
}
@Override
public void keyTyped(KeyEvent ke) {
check();
}
@Override
public void keyPressed(KeyEvent ke) {
check();
}
@Override
public void keyReleased(KeyEvent ke) {
check();
}
public void check(){
if(message.getText().equalsIgnoreCase(""))
send.setEnabled(false);
else
send.setEnabled(true);
}
public static void main(String[] args) {
chat chat1=new chat(9000,9001,1);
}
}
view raw chat.java hosted with ❤ by GitHub


Y ahora la clase que funciona comos servidor. Es un hilo que constantemente recibe y actualiza mensajes.
package chat;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class recepcion extends Thread{
chat cht;
int port;
public recepcion(chat cht, int port){
this.cht=cht;
this.port=port;
try {
servidor = new ServerSocket(port);
}
catch(IOException ex ){
System.out.println("puerto ocupado");
}
}
private Socket socket;
private ServerSocket servidor;
private ObjectInputStream flujo_entrada;
@Override
public void run(){
while(true){
String mensaje = null;
try{
socket =servidor.accept();
flujo_entrada = new ObjectInputStream(socket.getInputStream());
mensaje = (String) flujo_entrada.readObject();
if(!cht.history.getText().equalsIgnoreCase(""))
cht.history.setText(cht.history.getText()+"\n"+mensaje);
else
cht.history.setText(cht.history.getText()+mensaje);
socket.close();
}
catch(IOException | ClassNotFoundException ex){}
}
}
}
view raw recepcion.java hosted with ❤ by GitHub

miércoles, 1 de octubre de 2014

Los filosofos comensales (venganza con semaforos)

En mi nuevo intento asistido por internet, verán que mi lógica es muy similar a la anterior, con la excepción de que en lugar de las colas uso semaforos, y que ya se como crear threads en ciclos xD

Esta vez, cambian básicamente tres cosas:
Primero, ya no son tenedores, porque me pareció ridiculo que necesitaces dos tenedores para comer, así que lo cambié por palillos. Tiene sentido, ¿no?

Segundo, guardo los palillos a los que accede cada comensal en una matriz de enteros, donde por defecto el primer valor será el numero de identificación del comensal, y el segundo será 0 o 1, para cada palillo.

Tercero, SEMAFOROS. ¿por qué? Porque me dejo de preocupar por que se mezclen los permisos para tomar los palillos. En mi programa anterior, eran dos sentencias diferentes la que revisa la disponibilidad de los tenedores, y la que efectúa el cambio de disponibilidad, por lo que si dos hilos accedían casi al mismo tiempo a la revisión, era posible que uno de ellos tomara los tenedores e inmediatamente después el otro también, y luego ambos "reportaban" que los tenedores estaban tomados, y luego, muy cinicamente, se ponían a comer. Además así puedo especificar que solo un comensal puede sostener cada palillo.

Oh y también volví aleatorios los tiempos de retardo, porque noté que hay menos problemas así.
Ignoro la razón de esto último.

import java.util.concurrent.Semaphore;
public class ejemplo2 extends Thread {
private final int id;
private final Semaphore[] semaforo;
private final int[][] palillos;
private final int palilloL;
private final int palilloR;
// private int hambre=5;
public ejemplo2(int id, Semaphore[] semaforo, int[][] palillos){
this.id=id;
this.semaforo=semaforo;
this.palillos=palillos;
this.palilloL=palillos[id][0];
this.palilloR=palillos[id][1];
}
protected void comer(){
if(semaforo[palilloR].tryAcquire()){
if(semaforo[palilloR].tryAcquire()){
System.out.println("FILÓSOFO " + id + " ESTÁ COMIENDO. USA LOS PALILLOS "+palilloL+" Y "+palilloR);
// hambre--;
try{
int time=0;
while(time<=0)
time=new Random().nextInt()%2000;
sleep(time);
}catch (InterruptedException ex){
System.out.println("Error : " + ex.toString());
}
System.out.println("Filósofo " + id + " terminó de comer. Liberó los palillos " + palilloL + " y " + palilloR);
semaforo[palilloR].release();
}
semaforo[palilloL].release();
}else{
System.out.println("Filósofo " + id + " está hambriento.");
}
}
protected void pensar(){
System.out.println("Filósofo " + id + " está pensando.");
try{
int time=0;
while(time<=0)
time=new Random().nextInt()%2000;
sleep(time);
}catch(InterruptedException ex){
System.out.println("Error en el método pensar(): " + ex.toString());
}
}
@Override
public void run(){
while(true){
pensar();
comer();
// if(hambre<=0){
// System.out.println("Filosofo "+id+" está satisfecho");
// break;
// }
}
}
}
view raw ejemplo2.java hosted with ❤ by GitHub


En la siguiente clase notarán como cree la matriz de la que hablé antes. La gran diferencia en este caso con respecto a mi intento anterior, es que ya solo creo los 5 comensales, y ellos mismos funcionan como buffers.

package filosofos;
import java.util.concurrent.Semaphore;
public class ejemplo1 {
final static int nFilosofos = 5;
final static int[][] palillos = {
{0, 4}, // filosofo 0
{1, 0}, // filosofo 1
{2, 1}, // filosofo 2
{3, 2}, // filosofo 3
{4, 3} // filosofo 4
};
final static Semaphore[] semaforo=new Semaphore[nFilosofos];
public static void main(String[] args){
for (int i = 0; i < nFilosofos; i++){
semaforo[i]=new Semaphore(1);
}
for(int idFilosofo=0; idFilosofo<nFilosofos; idFilosofo++){
new ejemplo2(idFilosofo, semaforo, palillos).start();
}
}
}
view raw ejemplo1.java hosted with ❤ by GitHub


Y solo para que quede claro, sigo sin usar sincronización.

Los filosofos comensales (first try)

Aqui les traigo mi primer intento a codificar el conocido problema de los filosofos comensales.

"Cinco filosofos se sientan ante la mesa a comer. Cada uno necesita dos palillos para sujetar su alimento, pero solo hay 5 palillos en la mesa. Los filosofos se turnan entre pensar y comer, y siempre hay máximo dos de ellos comiendo"

La primera clase que les muestro es la del filosofo. Aqui básicamente tengo todo lo que requiere éste para cambiar entre pensar y comer. Lo que hace es revisar si ambos tenedores (un objeto por cada uno) están tomados, si no es así, los toma y luego se sitúa al final de la cola de espera.

package filosofos;
public class Filosofo extends Thread{
public int hambre;
public int muerte;
public int id;
public Tenedor t1;
public Tenedor t2;
public Filosofo(int id, int hambre, Tenedor t1, Tenedor t2){
this.id=id;
this.hambre=hambre;
muerte=hambre;
this.t1=t1;
this.t2=t2;
t1.Insertar(this);
t2.Insertar(this);
}
public void run() {
while(hambre>0){
System.out.println("Filosofo #"+id+" tiene hambre. ");
try{
Thread.sleep(id*500);
}
catch(InterruptedException e){
}
if(t1.Consultar()==this && t2.Consultar()==this && t1.taken==false && t2.taken==false){
t1.taken=true;
t2.taken=true;
// muerte+=3;
t1.Remover(); t1.Insertar(this);
t2.Remover(); t2.Insertar(this);
System.out.println("Filosofo #"+id+" toma los tenedores #"+t1.id+" y #"+t2.id+" y empieza a comer. ");
try{
Thread.sleep(10000);
}
catch(InterruptedException e){
}
hambre-=3;
System.out.println("Filosofo #"+id+" suelta los tenedores #"+t1.id+" y #"+t2.id);
t1.taken=false;
t2.taken=false;
}
else{
System.out.println("Filosofo #"+id+" se queda con hambre. ");
// muerte-=3;
// if(muerte<=0){
// System.out.println("El filosofo #"+id+" ha muerto de hambre");
// System.exit(0);
// }
}
}
System.out.println("Filosofo #"+id+" está satisfecho");
}
}
view raw Filosofo.java hosted with ❤ by GitHub


La clase que sigue es el tenedor. Mi clase main crea 5 de estos junto con 5 filosofos, y funciona con los metodos de colas circulares. Si el filosofo que quiere tomar el tenedor se encuentra hasta arriba de la cola en ambos tenedores, entonces puede tomarlos.

package filosofos;
public class Tenedor extends ColaCircular{
public boolean taken=false;
public int id;
public Tenedor(int id){
super(2);
this.id=id;
}
}
view raw Tenedor.java hosted with ❤ by GitHub


Lo que hace mi clase main es crear 5 de cada uno de estos, y luego los pone a trabajar.
Al principio me pareció una idea bastante buena lo de las colas, pero luego noté que en mi programa solo come un filosofo a la vez. Así que después de moverle por un rato decidí intentar con una estrategia diferente: semaforos. (lo cual es muy parecido a lo que hice, pero es mejor)
Eso lo veremos en el siguiente post.