Java Events And Threads

Joe

Thành viên VIP
21/1/13
2,885
1,288
113
JAVA newbies and career-jumpers who jump laterally into OO-Software development field are often lack of OO-imagination and Operation System understanding. Their thinking process is driven by examples and that causes a lot of troubles to them. Especially, OO-events and their listeners because JAVA events and Listeners are numerous and versatile. Further: JAVA allows developers to create their own events and their related listeners. All that requires, of course, an excellent OS-Understanding. An OS Understanding of their nature.

Event is a reaction triggered by an action. For example: If you strike a Guitar String (action) a sound is echoed (reaction). The OO Understanding here is the event that can only triggered by a certain object. The event is the sound and the certain object is the Guitar string. If you strike a brick, no sound is echoed and you hear nothing, right? Exactly like that, JAVA events are only meaningful and useful when they are correctly applied. If an event is wrongly applied its related listener stays mute (or hears nothing). Let me list the most common-used JAVA events, their listeners and where they should be applied.
PHP:
+----------------------+------------------------------------+--------------------------+
!  Event			   ! Listener						   ! applied to			   !
+----------------------+------------------------------------+--------------------------+
! ActionEvent		  ! ActionListener					 ! any JAVA Component that  !
!					  !									! requires an action.	  !
+----------------------+------------------------------------+--------------------------+
! KeyEvent			 ! KeyListener						! editable components like !
!					  !									! JTextField, JTextArea,.. !
+----------------------+------------------------------------+--------------------------+
! MouseEvent		   ! MouseListener					  ! any JAVA area			!
+----------------------+------------------------------------+--------------------------+
! ItemEvent			! ItemListener					   ! any JAVA component that  !
!					  !									! provides selection of	!
!					  !									! items (JComboBox, etc.)  !
+----------------------+------------------------------------+--------------------------+
The realization of Event-Listener is a straight-forward action. It depends on the requirements of your application:

- Direct implementation,
- Using Adapter,
- Embedded implementation (or Lambda Expression -up JDK8),
- Your own Listening object.

Whatever implementation you prefer, you should pay more intention on:

- Performance: better with direct implementation (using the keyword "implements")
- Adapter or your own listening object should be used if it is needed for several different JAVA components.
- Embedded or Lambda is good if event and listener are used only once on a certain JAVA component. The performance is second after direct-implementation.

Example:
Direct Implementation
PHP:
import javax.swing.*;
import java.awt.event.*;
import javax.sound.midi.*;
// Direct Implementation
public class WithInte extends JFrame implements ActionListener {
	public static void main(String... a) throws Exception {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		WithInte wi = new WithInte();
	}
	private MidiChannel midi;
	public WithInte() {
		setTitle("Direct With Interface");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JButton jA = new JButton("DRUM");
		jA.addActionListener(this);
		add("North", jA);
		setSize(100, 70);
		setLocation(100, 100);
		setVisible(true);
		try {
			Synthesizer syn = MidiSystem.getSynthesizer( );
			syn.open( );
			midi = syn.getChannels( )[9];
		} catch (Exception e) { }
	}
	// implement the Interface
	public void actionPerformed(ActionEvent a) {
		midi.noteOn(64, 500); // make a "BUM"
	}
}
Own Listening object
PHP:
import javax.swing.*;
import java.awt.event.*;
import javax.sound.midi.*;
// Own Listening Object
public class WithImpl extends JFrame {
	public static void main(String... a) throws Exception {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		WithImpl wi = new WithImpl();
	}
	private MidiChannel midi;
	public WithImpl() {
		setTitle("Direct With Implement");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JButton jA = new JButton("DRUM");
		jA.addActionListener(bum);
		add("North", jA);
		setSize(100, 70);
		setLocation(100, 100);
		setVisible(true);
		try {
			Synthesizer syn = MidiSystem.getSynthesizer( );
			syn.open( );
			midi = syn.getChannels( )[9];
		} catch (Exception e) { }
	}
	// My own listening object
	ActionListener bum = new ActionListener() {
		public void actionPerformed(ActionEvent a) {
			midi.noteOn(64, 500); // make a "BUM"
		}
	};
}
With Lambda Expression
PHP:
import javax.swing.*;
import java.awt.event.*;
import javax.sound.midi.*;
// With Lambda Expression
public class WithLamb extends JFrame {
	public static void main(String... a) throws Exception {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		WithLamb wl = new WithLamb();
	}
	private MidiChannel midi;
	public WithLamb() {
		setTitle("Direct With LambdaExp");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JButton jA = new JButton("DRUM");
		jA.addActionListener(a -> {
				midi.noteOn(64, 500); // make a "BUM"
		});
		add("North", jA);
		setSize(100, 70);
		setLocation(100, 100);
		setVisible(true);
		try {
			Synthesizer syn = MidiSystem.getSynthesizer( );
			syn.open( );
			midi = syn.getChannels( )[9];
		} catch (Exception e) { }
	}
}
Embedded Implementation
PHP:
import javax.swing.*;
import java.awt.event.*;
import javax.sound.midi.*;
// Embedded Implementation
public class WithIDir extends JFrame {
	public static void main(String... a) throws Exception {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		WithIDir wid = new WithIDir();
	}
	private MidiChannel midi;
	public WithIDir() {
		setTitle("Direct With DirImplmt");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JButton jA = new JButton("DRUM");
		jA.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent a) {
				midi.noteOn(64, 500); // make a "BUM"
			}
		});
		add("North", jA);
		setSize(100, 70);
		setLocation(100, 100);
		setVisible(true);
		try {
			Synthesizer syn = MidiSystem.getSynthesizer( );
			syn.open( );
			midi = syn.getChannels( )[9];
		} catch (Exception e) { }
	}
}
The different implementations produce different physical sizes (more codes). More codes mean more work. More work means more time. More time means long waiting. Long waiting means bad performance.

Further: Most SWING developers (especially newbies) usually don't care about the events that are bound to some SWING components (e.g. JTextField, JTextArea, JComboBox, etc.). But have they ever thought about the events ? What are the events ? How the events are triggered ? etc.

The first question is as clear as the sun's shining. Event's a happening that occurs unexpectedly and arbitrarily. The second question leads to the surmise that event is triggered by a ghost that lurks somewhere in the darkness and storms out when it's disturbed...A ghost ? Yes for the religious it's a ghost, but for JAVA developers it's just a THREAD. A (SWING) event thread to be exact.

When an event is a thread then it runs fully independent from the main thread (app) and sometimes event needs to be tamed or controlled. Otherwise your app might run amok and goes on the rampage. The following example with a JComboBox that needs to be set to the default item at position 0...and terror happens because the ghost is insulted impolitely.
PHP:
1 public class JavaBadEvent extends JFrame implements ItemListener {
2	 public static void main(String... a) throws Exception {
3		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
4		JavaBadEvent je = new JavaBadEvent();
5	}
6	private JTextField jtf;
7	private JComboBox<String> jcb;
8	private JLabel jlab;
9	public JavaBadEvent() {
10		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
11		jlab = new JLabel();
12		jcb = new JComboBox<String>("Item_1!Item_2!Item_3!Item_4".split("!"));
13		jcb.addItemListener(this);
14		add("North",(new JPanel()).add(jcb));
15		add("Center",(new JPanel()).add(jlab));
16		setSize(200, 100);
17		setLocation(150, 150);
18		setResizable(false);
19		setVisible(true);
20   }
21   public void itemStateChanged(ItemEvent e) {
22		jlab.setText("Your selected Item:"+(String)jcb.getSelectedItem());
23		jcb.setSelectedIndex(0);
24   }
}
Everything and every line look quite good and quite proper...but the terror


Why that ? Let study the codes:

- line 1: the class is an implementation of ItemListener, also an Event-Thread that listens to the CHANGE of Items.
- line 13: attach the ItemListener thread to JComboBox jcb
- line 21-24: the implementation of the required method. In this implementation the ItemListener thread is still active...and if any change is made (here: setSelectedItem) it will trigger another action...an endless loop arises till all resources are consumed: Terror occurs.

So, how can one set a JComboBox after a Selection of an item back to a predefined default without making the ItemListener thread going amok ? Well, first of all: Thread programming requires an understanding of Synchronization, then the Thread management. Let's take a brief look at the JComboBox< E > API1 and what we can see is that the method addItemListener(ItemListener aListener) has an "arch enemy" named: removeItemListener(ItemListener aListener). Let's modify the codes using the arch enemy and rename the codes as JavaGoodEvent (see line 22a and 23a)
PHP:
1 public class JavaGoodEvent extends JFrame implements ItemListener {
2	 public static void main(String... a) throws Exception {
3		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
4		JavaGoodEvent je = new JavaGoodEvent();
5	}
6	private JTextField jtf;
7	private JComboBox<String> jcb;
8	private JLabel jlab;
9	public JavaBadEvent() {
10		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
11		jlab = new JLabel();
12		jcb = new JComboBox<String>("Item_1!Item_2!Item_3!Item_4".split("!"));
13		jcb.addItemListener(this);
14		add("North",(new JPanel()).add(jcb));
15		add("Center",(new JPanel()).add(jlab));
16		setSize(200, 100);
17		setLocation(150, 150);
18		setResizable(false);
19		setVisible(true);
20   }
21   public void itemStateChanged(ItemEvent e) {
22a	   jcb.removeItemListener(this);
22		jlab.setText("Your selected Item:"+(String)jcb.getSelectedItem());
23		jcb.setSelectedIndex(0);
23a	   jcb.addItemListener(this);
24   }
}
and see what happens...
 
Sửa lần cuối: