Đa luồng trong APPLET

Trong các mục trước, chúng ta đã tlm cách chia một chương trình thành nhiều tác vụ con thực hiện đồng thời. Mồi tác vụ cần phải đặt vào hàm run() của lóp kế thừa từ lớp Thread Nhưng, nếu chúng ta muốn bổ sung hàm run () vào một lóp được kế thừa từ một lớp khác, ví dụ lóp MỵApplet, thì thực hiện như thế nào? Một lóp ứng dụng nhúng đã kế thừa từ JApplet (hoặc Applet), nên không thể kể thừa thêm một lóp cơ sở nữa. Trường họp này chúng ta phải cài đặt Runnable như trên đã phân tích.

Sử dựng luồng trong Java, ta có thể tạo ra một MyApplet chạy trong luồng riêng của nó mà không quấy rầy đến các thành phần khác trong hệ thống. Ta có thể chạy nhiều MỵApplet cùng một lúc trên một trang ứng dụng. Tuy nhiên cũng phải cấn trọng, nếu mở quá nhiều luồng, sẽ làm chương trình chạy chậm lại mặc dù các luồng đó chạy độc lập với nhau [3],

Ví dụ 1.6. Xây dựng Animation với nhiều luồng để nạp một dãy ảnh nhằm tạo ra phim hoạt hình. Vì muốn sử dụng đa luồng trong một lớp đã kế thừa từ một lớp khác với Thread, nên ta phải cài đặt Runnable.

public class Animation extends JApplet implements Runnable{

//. .

public void run(){

// Hoạt dông của luồng thực hiện ờ đây

>

}

Ta phải đảm bảo tạo ra các luồng và tham chiếu tới các đối tượng của Runnable trong toán tử tạo lập. Luồng đuợc tạo ra sẽ gọi hàm run ( ) của đối tưọmg tưong ứng. Lời gọi hàm này sẽ lại tự động gọi hàm start ( ) được nạp chồng trong applet, ví dụ: public class Animation extends JApplet implements Runnable{

//…

public void start(){

runner = new Thread(this);

runner.start();

showstatus(“Click to stop”);

}

H …

private Thread runner;

}

Đối so this trong toán tử tạo lập Thread (this) chi ra đối tượng mà hàm run ( ) phải gọi thực hiện khi luồng thực hiện đối với đối tượng Animation hiện thời.

Mục đích của chúng ta là hiển thị một dãy các ảnh liên tiếp để tạo ra cảm giác chuyển động của các đối tượng ảnh. Mỗi ảnh được hiển thị trong một khung (frame). Ta có thể đặt mỗi frame trong một file (tệp) ảnh riêng hoặc có thề đặt tất cả trong cùng tệp. Trong ví dụ này, chúng ta sừ dụng một tệp với 36 ảnh liên tiếp cùa một quả cầu quay. Ta sử dụng đối tượng cùa lớp MediaTracker để nạp ảnh. Việc nạp ảnh, nhất là ânh từ Internet là tương đối chậm.

Khi ảnh đã được nạp, ta phải vẽ nó.

g.drawlmage(image, 0, – i * imageHeight / imageCount, null);

Như vậy, chương trinh Animat ion đề hiền thị ảnh hoạt hình có thể viết đơn giàn như

sau.

// Animation, java

import java.awt.*;

import j ava.awt.image.* ;

import )ava.awt.event.* ;

import j avax.swing.* ;

import java.net.* ;

public class Animation extends JApplet

implements Runnable{ public void init(){

addMouseListener(new MouseAdapter{){

public void mousePress(MouseEvent evt)( if(runner == null) start (); else stop ( );

}

}) ;

try {

imageName = getParameter(“imagename”); i f(imageName == null) imageName = “”; imageCount = 1 ;

String param = getParameter(“imagecount”); if(param != null)

imageCount = Integer.parselnt(param);

}catch(Exception e){

showStatus(“Error : ” + e) ;

}

image = null; loadlmage();

}

public void loadlmage(){ try {

URL url = new URL ( getDocumentBase ( ), imaqeNairie) ; image = getlmage(url) ;

MediaTracker tracker = new MediaTracker(this);

tracker.addlmage(image, 0);

tracker.waitForlD(0);

imageW = image.getWidth(null);

imageH = image.getHeight(null);

resize(imageW, imageH / imageCount);

}catch(InterruptedException e){

showstatus(“Loading intrrepted!”);

}catch(MalformedURLException e)( showStatus(“Bad URL!”);

}

}

public void paintComponent(Graphics g){ if(image == null) return;

g.drawlmage(image, 0, -(imageH / imageCount) * current, null);

}

public void start () (

runner = new Thread(this);

runner.start();

showStatus(“Click to stop”);

}

public void stop(){

runner.interrupt(); runner = null;

showStatus(“Click to restart”);

}

public void run () { try {

while ( ! Thread. int(irrupted () ) ( repaint();

current – (current + 1) Thread.s Lsep(2 00) ;

}

}catch(InterruptedException e){ showStatus(“Ird errepted!”);

}

}

private int current ;

private int imageCount; private int imageW, imageH; private Image image; private string imageName; private Thread runner;

}

Chương trình Animation cần có tên cùa tệp ảnh và sổ lượng frame. Các thông số này có thể cung cấp bời thẻ PARAM

<applet code = “Animation.class” width = 100 height = 100>

<param name = imagename value = “globe.gif”>

<param name = imagecount value = “36”>

</applet>

Ví dụ 1.7. Trong nhiều môi trường lập trình ta cần biết thời gian hoặc đặt lại thời gian cùa chương trình ứng dụng. Chương trình sau đây hiển thi hệ thống các đồng hồ theo nhiều múi giờ khác nhau. Mỗi đồng hồ có một luồng riêng, do vậy cần phải xây dựng lớp Timer kế thừa Thread Chương trình ClockGroup thực hiện sẽ hiển thị các đồng hồ quốc tế như hình

Hình 1.6. Hệ thổng đồng hồ quốc té

/* ClockGroup.java * Hiển thị hệ thống đồng hồ quốc tế

V

import java.awt.*; import java.awt.event .* ; import javax.swing.*; import java.util.*; import javax.swing.Timer; import java.text.*;

public class ClockGroup extends JApplet implements ActìonLìstener{

m

private ClockPanel clockPl, clockP2, clockP3; private JButton btResum, btSus; public static void main(String[] arg){

JFrame fr = new JFrame(“Clock Group Demo”); ClockGroup app = new ClockGroup();

fr.getContentPane().add(app, BorderLayout.CENTER);

app.init();

app.start ();

fr.setsize(600, 350);

fr.setvisible(true);

}

public void init(){

//Tạo ra Panel pi để chứa 3 đồng hồ

JPanel pi = new JPanel();

pi.setLayout(new GridLayout(1,3));

// Tạo lập đồng hồ Berlin

pi.add(clockPl = new ClockPanel()); clockPl.setTitle(“Berlin”); clockPl.clock.setTimeZonelD(“ECT”); clockPl.clock.setLocale(Locale.GERMAN) ;

// Tạo lập đồng hồ SanFrancisco pi.add(clockP2 = new ClockPanel ()); clockP2.setTitle(“San Francisco”); clockP2.clock.setTimeZoneID(“PST”); clockP2.clock.setLocale(Locale.US);

//Tạo lập đồng hồ Taipei

pi.add(clockP3 = new ClockPanel()) ; clockP3.setTitle V”Taipei”) ; clockP3.clock.setLocale(Locale.CHINESE); clockP3.clock.setTimeZoneID(“CTT”);

//Panel p2 để chứa 2 nhóm núl JPanel p2 = new JPanel() ; p2.setLayout(new FlowLayout());

p2.add(btResum = new JButton(“Resume All”)); p2.add(btSus = new JButton(“Suspend All”));

//Đưapi, p2 vào Applet

getContentPane ().setLayout(new BorderLayout()); getContentPane().add(pi,BorderLayout.CENTER); getContentPane().add(p2,BorderLayout.SOUTH);

// Đăng ký listener

btResum.addActionListener(this); btSus.addActionListener(this);

}

public void actionPerformed(ActionEvent e){ if(e.getSource () == btResum){

// Khởi động tat cả các đồng ho clockPl.resume(); clockP2.resume(); clockP3.resume();

}

else if (e.getSource() == btSus){

// Tạm dừng các đồng hồ

clockPl.suspend(); clockP2.suspend(); clockP3.suspend();

}

}

}

class ClockPanel extends JPanel implements ActionListener( private JLabel labelT; protected Clock clock = null; private JButton btResum, btSus; public ClockPanelO {

JPanel bt = new JPanel();

bt.add(btResum = new JButton(“Resum”));

bt-. add (btSus = new JButton (“Suspend” )) ;

// Đặt Border Layout cho ClockPanel setLayout(new BorderLayout());

// Đưa tiêu đề ờ phía trên

add(labelT = new JLabelO, BorderLayout.NORTH) ; labelT.setHorizontalAlignment(JLabel.CENTER);

// Đưa đồng hồ vào giữa panel

add(clock = new Clock 0,BorderLayout.CENTER); add(bt,BorderLayout.SOUTH);

// Lắng nghe các sự kiện

btResum.addActionListener(this); btSus.addActionListener(this) ;

}

public void setTitle (String title)! labeIT.setText (title) ;

}

public void actionPerformed(ActionEvent e)( if (e.getSource() == btResum){ clock.resume();

>

else if (e.getSource() == btSus){ clock. suspend () ;

}

}

public void resume(){

if(clock != null) clock.resume();

}

public void suspendí){

if(clock != null) clock.suspend();

}

}

class Clock extends StillClock implements ActionListeneri protected Timer timer; public Clock(){

this (Locale.getDefault(), TimeZone.getDefault());

>

public Clock(Locale 1, TimeZone t){

super(1, t) ;

timer = new Timer(1000,this); timer.start();

>

public void resume(){ timer.start();

}

public void suspendí){ timer.stop() ;

}

public void actionPerformed(ActionEvent e){ repaint();

}

}

// Hiển thị đồng hồ ờ JPanel class StillClock extends JPanel{ protected TimeZone timeZ; protected int xC, yC; protected int clockR; protected DateFormat form; public StillClockO {

this(Locale.getDefault(), TimeZone.getDefault());

}

public StillClock(Locale 1, TimeZone t){ setLocale í 1); this.timeZ = t;

}

public void setTimeZonelD(String newT){ timeZ = TimeZone.getTimeZone(newT);

  • }

//Viết đề pa int Component ( ) để hiển thị đồng hồ

public void paintComponent(Graphics g){ super.paintComponent(g);

Chương 7 LẬP TRÌNH ĐA LUỒNG

clockR=(int) (Math.ĩĩLÌri(getsize() .width,getsize() .height) *0.7*0.5) ; xC = (getsize().width)/2; yC = (getsize().height)/2;

// Vẽ hình tròn

g.setColor(Color.black) ;

g.drawCXral (xC-clockR, yC – clockR, 2*clockR, 2*clockR); g.drawstring ( “12”, xC – 5, yC – clockR); g.drawstring ( “9”, xC – clockR-10, yC + 3); g.drawstring(“3”, xC + clockR, yC + 3); g.drawstring(“6”, xC +3, yC + clockR + 10);

// Đọc thời gian từ mảy tính sử dụng GregorianCalendar GregorianCaìendar cal = new GregorianCalendar(timeZ);

//Vẽ kim giây

int second = (int) cal .get (GregorianCalendar. SECOND) ; int sLen = (int) (clockR *0.9);

int xS = (int)(xC+sLen*Math. sin (second*(2*Math.PI/60))); int yS = (int)(yC-sLen*Math.cos(second*(2*Math.PI/6Q))); g. ietColor(Color.red); g.drawLine (xC, yC, xS, yS);

// Vẽ kim phút

int minute = (int) cal.get (GregorianCalendar.MINUTE); int mLen = (int)(clockR *0.75);

int xM = (int) (xC+mLen*hbth. sin (minute* (2*Math.PI/60) ) ) ; int = (ínt) (yC-mLen*Math.cos (minute* (2*Math. PI/60))); g . setColor(Color.blue); g.drawLine(xC, yC, xM, ỵM);

//Vê kim giờ

int hour = (int) cal.get (GregorianCalendar.HOUR_OF_DAY); int hLen = (int)(clockR *0.6);

int xH = (int) (xC+hLen*hfeth.sin( (hour4ĩni.nute/60.0) * (2*Pfeith.PI/ĩ2))); int ỊịÍĩ – (int) (yC-hLen*Math.oos ((hour4rninute/60.0) * (2*Pfeth.PI/12))); g.setColor{Color.black); g.drawLine(xC, yC, xH, yH) ;

// Đặt chế độ hiển thị theo múi giờ địa phưong và kiểu hiển thị

form = DateFormat.getDateTimelnstance (

DateFormat.MEDIUM,DateFormat.LONG, getLocale()); form.setTimeZone(timeZ);

// Hiền thị ngáy giở

String today = form.format(cal.getTime());

FontMetrics fm = g.getFontMetrics();

g.drawstring (today, (getsize () .width-fin. stringWidth (today)) /2, C+clockR+30) ;