0% found this document useful (0 votes)
36 views

09 MultiThreadedProgramming

This document discusses multithreaded programming in Java. It covers thread fundamentals such as process-based vs thread-based multitasking, advantages of multithreading, thread states, and the Thread class and Runnable interface. It then demonstrates how to create threads by implementing Runnable and extending Thread, including starting and running threads. Finally, it shows improved and alternative approaches to thread creation.

Uploaded by

Vân Trần
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
36 views

09 MultiThreadedProgramming

This document discusses multithreaded programming in Java. It covers thread fundamentals such as process-based vs thread-based multitasking, advantages of multithreading, thread states, and the Thread class and Runnable interface. It then demonstrates how to create threads by implementing Runnable and extending Thread, including starting and running threads. Finally, it shows improved and alternative approaches to thread creation.

Uploaded by

Vân Trần
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 56

JAVA PROGRAMMING

Week 9: Multithreaded
Programming
Lecturer: NGUYỄN Thị Minh Tuyền
Plan 2

1. Multithreading fundamentals
2. Synchronization

Java Programming
Plan 3

1. Multithreading fundamentals
2. Synchronization

Java Programming
Multithreading fundamentals 4

• Two distinct types of multitasking:


• process-based and
• thread-based.
• Process-based multitasking is the feature that allows your
computer to run two or more programs concurrently.
• A program is the smallest unit of code that can be dispatched by the
scheduler.
• In a thread-based multitasking environment, the thread is
the smallest unit of dispatchable code.
• A single program can perform two or more tasks at once.
• Process-based multitasking is not under the control of Java.
Multithreaded multitasking is.

Java Programming
Advantage of multithreading 5

• It enables you to write very efficient programs because it


lets you utilize the idle time that is present in most
programs.
• A program will often spend a majority of its execution time waiting
to send or receive information to or from a device.
• By using multithreading, your program can execute another task
during this idle time.
• Java’s multithreading features work in both single-core and
multi-core systems.
• In a single-core system: concurrently executing threads share the
CPU, with each thread receiving a slice of CPU time à two or more
threads do not actually run at the same time, but idle CPU time is
utilized.
• In multiprocessor/multicore systems: Two or more threads can be
executed simultaneously.
Java Programming
States of a thread 6

• A thread can be in one of several states:


• running,
• suspended (a temporary halt to its execution)
• resumed,
• blocked (when waiting for a resource),
• terminated.
• Synchronization allows the execution of threads to be
coordinated in certain well-defined ways.

Java Programming
Thread class and Runnable
interface 7

• Thread class and its companion interface, Runnable are


packaged in java.lang.
• Thread encapsulates a thread of execution.
• To create a new thread: your program will either extend
Thread or implement the Runnable interface.

Java Programming
Creating a thread [1] 8

• A thread is created by instantiating an object of type Thread.


• Thread class encapsulates an object that is runnable.
• Two ways to create a runnable object:
• Implement the Runnable interface.
• Extend the Thread class.
• Remember: Both approaches still use the Thread class to
instantiate, access, and control the thread. The only
difference is how a thread-enabled class is created.

Java Programming
Creating a thread [2] 9

• Runnable interface abstracts a unit of executable code.


• You can construct a thread on any object that implements the
Runnable interface.
• Runnable defines only one method called run():
public void run()
• Inside run(), you will define the code that constitutes the new
thread.
• run() can also call other methods, use other classes, and declare
variables just like the main thread.
• The only difference: run() establishes the entry point for another,
concurrent thread of execution within your program.
• This thread will end when run() returns.

Java Programming
Creating a thread [3] 10

• After having created a class that implements Runnable, you


will instantiate an object of type Thread on an object of that
class.
• Constructor:
Thread(Runnable threadOb)
• threadOb : an instance of a class that implements the Runnable
interface. This defines where execution of the thread will begin.
• The start() method: void start() makes the new
thread start running when it is invoked
• In essence, start() executes a call to run().

Java Programming
11

• sleep() method causes the thread from which it is called to


suspend execution for the specified period of milliseconds.

static void sleep(long milliseconds)


throws InterruptedException

• milliseconds: the number of milliseconds to suspend.

Java Programming
1. class MyThread implements Runnable {
2.

3.
String thrdName;
MyThread(String name) {
12
4. thrdName = name;
5. }
6. // Entry point of thread.
7. public void run() {
8. System.out.println(thrdName + " starting.");
9. try {
10. for (int count = 0; count < 10; count++) {
11. Thread.sleep(400);
12. System.out.println("In " + thrdName +
13. ", count is " + count);
14. }
15. } catch (InterruptedException exc) {
16. System.out.println(thrdName + " interrupted.");
17. }
18. System.out.println(thrdName + " terminating.");
19. }
Java Programming
20. }
1. class UseThreads {
2. public static void main(String args[]) { 13
3. System.out.println("Main thread starting.");
4. // First, construct a MyThread object.
5. MyThread mt = new MyThread("Child #1");
6. // Next, construct a thread from that object.
7. Thread newThrd = new Thread(mt);
8. // Finally, start execution of the thread.
9. newThrd.start();
10. for (int i = 0; i < 50; i++) {
11. System.out.print(".");
12. try {
13. Thread.sleep(100);
14. } catch (InterruptedException exc) {
15. System.out.println("Main thread interrupted.");
16. }
17. }
18. System.out.println("Main thread ending.");
19. }
Java Programming
20. }
14

Java Programming
1. class MyThread12 implements Runnable{
2. Thread thrd;
3. MyThread12(String name){ thrd = new Thread(this, name); }
4. public static MyThread12 createAndStart(String name) {
5. MyThread12 myThrd = new MyThread12(name);
myThrd.thrd.start(); // start the thread
6.
7. return myThrd; Improved
8.
9.
}
@Override
version
10. public void run() {
11. System.out.println(thrd.getName() + " starting.");
12. try {
13. for(int count = 0; count < 10; count++) {
14. Thread.sleep(400);
15. System.out.println("In " + thrd.getName() +
16. ", count is " + count);
17. }
18. }catch(InterruptedException exc) {
19. System.out.println(thrd.getName() + " interrupted.");
20. }
21. System.out.println(thrd.getName() + " terminating.");
22. }
Java Programming
23. }
16

1. public class ThreadVariations {


2. public static void main(String[] args) {
3. System.out.println("Main thread starting.");
4. // Create and start a thread.
5. MyThread12 mt = MyThread12.createAndStart("Child #1");
6. for(int i =0; i < 50; i++) {
7. System.out.print(".");
8. try {
9. Thread.sleep(100);
10. }catch(InterruptedException exc) {
11. System.out.println("Main thread interruped.");
12. }
13. }
14. System.out.println("Main thread ending.");
15. }
Java Programming
16. }
1. class MyThread1 extends Thread {
2. MyThread1(String name) { 17
3. super(name); // name thread
4. start(); // start the thread
5. }
6. public void run() {
7. System.out.println(getName() + " starting.");
8. try {
9. for (int count = 0; count < 10; count++) {
10. Thread.sleep(400);
11. System.out.println("In " + getName() +
12. ", count is " + count);
13. }
14. }catch (InterruptedException exc) {
15. System.out.println(getName() + " interrupted.");
16. }
17. System.out.println(getName() + " terminating.");
18. }
19. } Java Programming
18
1. class ExtendThread {
2. public static void main(String args[]) {
3. System.out.println("Main thread starting.");
4. MyThread1 mt = new MyThread1("Child #1");
5. for (int i = 0; i < 50; i++) {
6. System.out.print(".");
7. try {
8. Thread.sleep(100);
9. } catch (InterruptedException exc) {
10. System.out.println("Main thread interrupted.");
11. }
12. }
13. System.out.println("Main thread ending.");
14. }
15. } Java Programming
Creating multiple threads 19

Java Programming
1. class MyThread2 implements Runnable {
2.

3.
Thread thrd;
MyThread2(String name) {
20
4. thrd = new Thread(this, name); thrd.start();
5. }
6. public void run() {
7. System.out.println(thrd.getName() + " starting.");
8. try {
9. for (int count = 0; count < 10; count++) {
10. Thread.sleep(400);
11. System.out.println("In " + thrd.getName()
12. + ", count is " + count);
13. }
14. } catch (InterruptedException exc) {
15. System.out.println(thrd.getName()
16. + " interrupted.");
17. }
18. System.out.println(thrd.getName() + " terminating.");
19. }
Java Programming
20. }
1. class MoreThreads {
2. public static void main(String args[]) { 21
3. System.out.println("Main thread starting.");
4.
5. MyThread2 mt1 = new MyThread2("Child #1");
6. MyThread2 mt2 = new MyThread2("Child #2");
7. MyThread2 mt3 = new MyThread2("Child #3");
8.
9. for (int i = 0; i < 50; i++) {
10. System.out.print(".");
11. try {
12. Thread.sleep(100);
13. } catch (InterruptedException exc) {
14. System.out.println("Main thread interrupted.");
15. }
16. }
17. System.out.println("Main thread ending.");
18. }
Java Programming
19. }
22

Java Programming
Determining when a thread ends 23

• It is often useful to know when a thread has ended.


• Two means to determine if a thread has ended.
final boolean isAlive()
• Returns true if the thread upon which it is called is still running.
• It returns false otherwise.

final void join() throws InterruptedException


• Waits until the thread on which it is called terminates.
• Additional forms of join() allow you to specify a maximum amount
of time that you want to wait for the specified thread to terminate.

Java Programming
1. class MoreThreads {
2. /* version 2*/ 24
3. public static void main(String args[]) {
4. System.out.println("Main thread starting.");
5. MyThread2 mt1 = new MyThread2("Child #1");
6. MyThread2 mt2 = new MyThread2("Child #2");
7. MyThread2 mt3 = new MyThread2("Child #3");
8. do {
9. System.out.print(".");
10. try { Thread.sleep(100);
11. }catch(InterruptedException exc) {
12. System.out.println("Main thread interrupted.");
13. }
14. } while (mt1.thrd.isAlive() ||
15. mt2.thrd.isAlive() ||
16. mt3.thrd.isAlive());
17. System.out.println("Main thread ending.");
18. }
Java Programming
19. }
1. class MyThread3 implements Runnable {
2.
3.
Thread thrd;
MyThread3(String name) {
25
4. thrd = new Thread(this, name);thrd.start();
5. }
6. public void run() {
7. System.out.println(thrd.getName() + " starting.");
8. try {
9. for (int count = 0; count < 10; count++) {
10. Thread.sleep(400);
11. System.out.println("In " + thrd.getName()
12. + ", count is " + count);
13. }
14. } catch (InterruptedException exc) {
15. System.out.println(thrd.getName()
16. + " interrupted.");
17. }
18. System.out.println(thrd.getName() + " terminating.");
19. }
20. } Java Programming
1. class JoinThreads {
2.
3.
public static void main(String args[]) {
System.out.println("Main thread starting.");
26
4. MyThread3 mt1 = new MyThread3("Child #1");
5. MyThread3 mt2 = new MyThread3("Child #2");
6. MyThread3 mt3 = new MyThread3("Child #3");
7. try {
8. mt1.thrd.join();
9. System.out.println("Child #1 joined.");
10. mt2.thrd.join();
11. System.out.println("Child #2 joined.");
12. mt3.thrd.join();
13. System.out.println("Child #3 joined.");
14. } catch (InterruptedException exc) {
15. System.out.println("Main thread interrupted.");
16. }
17. System.out.println("Main thread ending.");
18. }
19. }
Java Programming
27

Java Programming
Thread priorities 28

• Each thread has associated with it a priority setting.


• A thread’s priority determines, in part, how much CPU time a thread
receives relative to the other active threads.
• In general: over a given period of time, low-priority threads
receive little. High-priority threads receive a lot.
• It is important to understand that another factors also affect
how much CPU time a thread receives.
• When a child thread is started, its priority setting is equal to
that of its parent thread.
• You can change a thread’s priority by
final void setPriority(int level)
level specifies the new priority setting for the calling thread.

Java Programming
29

• The value of level must be within the range MIN_PRIORITY


(1) and MAX_PRIORITY (10).
• Default priority: NORM_PRIORITY (5)

• You can obtain the current priority setting by


final int getPriority()

Java Programming
1. class Priority implements Runnable {
2. int count; Thread thrd; 30
3. static boolean stop = false; static String currentName;
4. Priority(String name) {
5. thrd = new Thread(this, name); count = 0;
6. currentName = name;
7. }
8. public void run() {
9. System.out.println(thrd.getName() + " starting.");
10. do { count++;
11. if (currentName.compareTo(thrd.getName()) != 0) {
12. currentName = thrd.getName();
13. System.out.println("In " + currentName);
14. }
15. } while (stop == false && count < 10000000);
16. stop = true;
17. System.out.println("\n" + thrd.getName()
18. + " terminating.");
19. }
Java Programming
20. }
1. class PriorityDemo {
2. public static void main(String args[]) { 31
3. Priority mt1 = new Priority("High Priority");
4. Priority mt2 = new Priority("Low Priority");
5. Priority mt3 = new Priority("Normal Priority #1");
6. Priority mt4 = new Priority("Normal Priority #2");
7. Priority mt5 = new Priority("Normal Priority #3");
8. mt1.thrd.setPriority(Thread.NORM_PRIORITY + 2);
9. mt2.thrd.setPriority(Thread.NORM_PRIORITY - 2);
10. mt1.thrd.start(); mt2.thrd.start();
11. mt3.thrd.start(); mt4.thrd.start(); mt5.thrd.start();
12. try {
13. mt1.thrd.join(); mt2.thrd.join();
14. mt3.thrd.join(); mt4.thrd.join(); mt5.thrd.join();
15. } catch (InterruptedException exc) {
16. System.out.println("Main thread interrupted.");
17. }
Java Programming
18. ....
32

1. ...
2. System.out.println("\nHigh priority thread counted to
3. " + mt1.count);

4. System.out.println("Low priority thread counted to "


+ mt2.count);
5.
System.out.println("1st Normal priority thread
6.
counted to " + mt3.count);
7.
System.out.println("2nd Normal priority thread
8. counted to " + mt4.count);
9. System.out.println("3rd Normal priority thread
10. counted to " + mt5.count);
11. }
12. }

Java Programming
Plan 33

1. Multithreading fundamentals
2. Synchronization

Java Programming
Synchronization [1] 34

• When using multiple threads, it is sometimes necessary


to coordinate the activities of two or more à
synchronization.
• Reason for using synchronization:
• When two or more threads need access to a shared resource
that can be used by only one thread at a time.
• When one thread is waiting for an event that is caused by
another thread.
• Key to synchronization: monitor (controls access to an
object)
• A monitor works by implementing the concept of a lock.
• When an object is locked by one thread, no other thread can gain
access to the object. When the thread exits, the object is
unlocked and is available for use by another thread.
Java Programming
Synchronization [2] 35

• All objects in Java have a monitor.


• This feature is built into the Java language itself.
àall objects can be synchronized.
• Keyword: synchronized.
• Two ways to synchronize:
• synchronized method & synchronized statement.
• both use the synchronized keyword.

Java Programming
Synchronized methods 36

• Use the synchronized keyword.


• When that method is called: the calling thread enters the
object’s monitor, which then locks the object.
• While locked: no other thread can enter the method, or enter
any other synchronized method defined by the object’s class.
• When the thread returns from the method: the monitor
unlocks the object, allowing it to be used by the next thread.
à synchronization is achieved with virtually no programming
effort on your part.

Java Programming
1. class SumArray {
2. private int sum; 37
3. synchronized int sumArray(int nums[]) {
4. sum = 0; // reset sum
5. for (int i = 0; i < nums.length; i++) {
6. sum += nums[i];
7. System.out.println("Running total for "
8. + Thread.currentThread().getName()
9. + " is " + sum);
10. try {
11. Thread.sleep(10); // allow task-switch
12. } catch (InterruptedException exc) {
13. System.out.println("Thread interrupted.");
14. }
15. }
16. return sum;
17. }
Java Programming
18. }
1. class MyThread4 implements Runnable {
2. Thread thrd; 38
3. static SumArray sa = new SumArray();
4. int a[]; int answer;
5. MyThread4(String name, int nums[]) {
6. thrd = new Thread(this, name);
7. a = nums; thrd.start(); // start the thread
8. }
9. public void run() {
10. int sum;
11. System.out.println(thrd.getName() + " starting.");
12. answer = sa.sumArray(a);
13. System.out.println("Sum for " + thrd.getName()
14. + " is " + answer);
15. System.out.println(thrd.getName() + " terminating.");
16. }
17. }
Java Programming
39

1. class Sync {
2. public static void main(String args[]) {
3. int a[] = { 1, 2, 3, 4, 5 };
4. MyThread4 mt1 = new MyThread4("Child #1", a);
5. MyThread4 mt2 = new MyThread4("Child #2", a);
6.

7. try {
8. mt1.thrd.join();
9. mt2.thrd.join();
10. } catch (InterruptedException exc) {
11. System.out.println("Main thread interrupted.");
12. }
13. }
14. } Java Programming
40

Java Programming
Synchronized statement 41

• Although creating synchronized methods within classes that


you create is an easy and effective means of achieving
synchronization, it will not work in all cases.
• Example: you might want to synchronize access to some
method that is not modified by synchronized.
• This can occur because you want to use a class that was not created
by you but by a third party, and you do not have access to the source
code.
àIt is not possible for you to add synchronized to the
appropriate methods within the class : simply put calls to the
methods defined by this class inside a synchronized block.

Java Programming
42

• General form of a synchronized block:

synchronized(objref) {
// statements to be synchronized
}

• objref is a reference to the object being synchronized.


• Once a synchronized block has been entered, no other thread can
call a synchronized method on the object referred to by objref until
the block has been exited.

Java Programming
1. class SumArray1 {
2. private int sum; 43
3. int sumArray1(int nums[]) {
4. sum = 0; // reset sum
5. for (int i = 0; i < nums.length; i++) {
6. sum += nums[i];
7. System.out.println("Running total for "
8. + Thread.currentThread().getName()
9. + " is " + sum);
10. try {
11. Thread.sleep(10); // allow task-switch
12. } catch (InterruptedException exc) {
13. System.out.println("Thread interrupted.");
14. }
15. }
16. return sum;
17. }
Java Programming
18. }
1. class MyThread5 implements Runnable {
2. Thread thrd; 44
3. static SumArray sa = new SumArray();
4. int a[]; int answer;
5. MyThread5(String name, int nums[]) {
6. thrd = new Thread(this, name);
7. a = nums; thrd.start(); // start the thread
8. }
9. public void run() {
10. int sum;
11. System.out.println(thrd.getName() + " starting.");
12. // synchronize calls to sumArray()
13. synchronized (sa) { answer = sa.sumArray(a); }
14. System.out.println("Sum for " + thrd.getName()
15. + " is " + answer);
16. System.out.println(thrd.getName() + " terminating.");
17. }
Java Programming
18. }
45

1. class Sync1 {
2. public static void main(String args[]) {
3. int a[] = { 1, 2, 3, 4, 5 };
4. MyThread5 mt1 = new MyThread5("Child #1", a);
5. MyThread5 mt2 = new MyThread5("Child #2", a);
6.

7.
try {

8.
mt1.thrd.join();

9. mt2.thrd.join();

10. } catch (InterruptedException exc) {

11.
System.out.println("Main thread interrupted.");

12.
}

13. }

14. } Java Programming


Thread communication 46

• A thread called T is executing inside a synchronized method


and needs access to a resource called R that is temporarily
unavailable. What should T do?
• If T enters some form of polling loop that waits for R, T ties up the
object, preventing other threads’ access to it à partially defeats the
advantages of programming for a multithreaded environment.
• A better solution: have T temporarily relinquish control of the object,
allowing another thread to run. When R becomes available, T can be
notified and resume execution à interthread communication (one
thread can notify another that it is blocked and be notified that it can
resume execution).
• Java supports interthread communication with the wait(),
notify(), and notifyAll() methods.

Java Programming
47

final void wait()


throws InterruptedException
final void wait(long millis)
throws InterruptedException
final void wait(long millis, int nanos)
throws InterruptedException
final void notify()
resumes one waiting thread
final void notifyAll()
notifies all threads, with the scheduler determining
which thread gains access to the object.
Java Programming
1. class TickTock {
2.
3.
String state; // contains the state of the clock
synchronized void tick(boolean running) {
48
4. if (!running) { state = "ticked"; notify(); return; }
5. System.out.print("Tick ");
6. state = "ticked"; notify();
7. try { while (!state.equals("tocked")) wait();
8. } catch (InterruptedException exc) {
9. System.out.println("Thread interrupted.");
10. }
11. }
12. synchronized void tock(boolean running) {
13. if (!running) { state = "tocked"; notify(); return; }
14. System.out.println("Tock");
15. state = "tocked"; notify();
16. try { while (!state.equals("ticked")) wait();
17. } catch (InterruptedException exc) {
18. System.out.println("Thread interrupted.");
19. }
20. }
21. } Java Programming
1. class MyThread6 implements Runnable {
2. Thread thrd; TickTock ttOb; 49
3. MyThread6(String name, TickTock tt) {
4. thrd = new Thread(this, name);
5. ttOb = tt; thrd.start(); // start the thread
6. }
7. public void run() {
8. if (thrd.getName().compareTo("Tick") == 0) {
9. for (int i = 0; i < 5; i++)
10. ttOb.tick(true);
11. ttOb.tick(false);
12. } else {
13. for (int i = 0; i < 5; i++)
14. ttOb.tock(true);
15. ttOb.tock(false);
16. }
17. }
Java Programming
18. }
50

1. class ThreadCom {
2. public static void main(String args[]) {
3. TickTock tt = new TickTock();
4. MyThread6 mt1 = new MyThread6("Tick", tt);
5. MyThread6 mt2 = new MyThread6("Tock", tt);
6.

7. try {
8. mt1.thrd.join();
9. mt2.thrd.join();
10. } catch (InterruptedException exc) {
11. System.out.println("Main thread interrupted.");
12. }
13. }
14. } Java Programming
Deadlock, race condition 51

• Deadlock is, as the name implies, a situation in which one


thread is waiting for another thread to do something, but that
other thread is waiting on the first à both threads are
suspended, waiting on each other, and neither executes.
• A race condition occurs when two (or more) threads attempt
to access a shared resource at the same time, without proper
synchronization.

Java Programming
Suspending, resuming, and
stopping threads 52

• final void resume()


• final void suspend()
• final void stop()
• While these methods seem to be a perfectly reasonable and
convenient approach to managing the execution of threads,
they must no longer be used.
• Deprecated by Java 2.

Java Programming
1. class MyThread8 implements Runnable {
2.

3.
Thread thrd; boolean suspended; boolean stopped;
MyThread8(String name) {
53
4. thrd = new Thread(this, name);
5. suspended = false; stopped = false; thrd.start();
6. }
7. public void run() {
8. System.out.println(thrd.getName() + " starting.");
9. try {
10. for (int i = 1; i < 1000; i++) {
11. System.out.print(i + " ");
12. if ((i % 10) == 0) {
13. System.out.println(); Thread.sleep(250);
14. }
15. synchronized (this){
16. while(suspended){wait();}
17. if(stopped) break;
18. }
Java Programming
19. }
1. } catch (InterruptedException exc) {
2.
54
System.out.println(thrd.getName() + " interrupted.");
3. }
4. System.out.println(thrd.getName() + " exiting.");
5. }
6. synchronized void mystop() {
7. stopped = true;
8. suspended = false;
9. notify();
10. }
11. synchronized void mysuspend() {
12. suspended = true;
13. }
14. synchronized void myresume() {
15. suspended = false;
16. notify();
17. }
Java Programming
18. }
1. class Suspend {
2. public static void main(String args[]) { 55
3. MyThread8 ob1 = new MyThread8("My Thread");
4. try {
5. Thread.sleep(1000);
6. //let ob1 thread start executing
7. ob1.mysuspend();
8. System.out.println("Suspending thread.");
9. Thread.sleep(1000);
10.

11.
ob1.myresume();

12.
System.out.println("Resuming thread.");

13. Thread.sleep(1000);

14.

15.
ob1.mysuspend();

16.
System.out.println("Suspending thread.");

17. Thread.sleep(1000);
Java Programming
1. ob1.myresume();
2. System.out.println("Resuming thread."); 56
3. Thread.sleep(1000);
4.

5. ob1.mysuspend();
6. System.out.println("Stopping thread.");
7. ob1.mystop();
8. } catch (InterruptedException e) {
9. System.out.println("Main thread Interrupted");
10. }
11. try {
12. ob1.thrd.join();
13. } catch (InterruptedException e) {
14. System.out.println("Main thread Interrupted");
15. }
16. System.out.println("Main thread exiting.");
17. }
Java Programming
18. }

You might also like