Java Threads

Some Concepts in Thread Control

Here I would like to list some concepts I used to be not aware of.

Daemon Threads

Daemon threads are service providers for other threads running in the same process as the daemon thread. The run() method for a daemon thread is typically an infinite loop that waits for a service request.

 

When the only remaining threads in a process are daemon threads, the interpreter exits. This makes sense because when only daemon threads remain, there is no other thread for which a daemon thread can provide a service.

 

Any Java thread can be a daemon thread. To specify that a thread is a daemon thread, call the setDaemon method with the argument true. To determine if a thread is a daemon thread, use the accessor method isDaemon.

Thread Priority

When a Java thread is created, it inherits its priority from the thread that created it. You can also modify a thread's priority at any time after its creation using the setPriority method. Thread priorities are integers ranging between MIN_PRIORITY and MAX_PRIORITY (constants defined in the Thread class). The higher the integer, the higher the priority.

 

At any given time, when multiple threads are ready to be executed, the runtime system chooses the runnable thread with the highest priority for execution. Only when that thread stops, yields, or becomes not runnable for some reason will a lower priority thread start executing. If two threads of the same priority are waiting for the CPU, the scheduler chooses one of them to run in a round-robin fashion. The chosen thread will run until one of the following conditions is true:

  • A higher priority thread becomes runnable.
  • It yields, or its run method exits.
  • On systems that support time-slicing, its time allotment has expired.

Then the second thread is given a chance to run, and so on, until the interpreter exits.

Selfish Threads

In some Threads, there could be a while(true) condition block to execute some behavior infiity.  For example:

 

public int token = 1;
public void run() {
    while (token < n) {
        token++;
    }
} 

 

The while loop in the run method is in a tight loop. Once the scheduler chooses a thread with this thread body for execution, the thread never voluntarily relinquishes control of the CPU--the thread continues to run until the while loop terminates naturally or until the thread is preempted by a higher priority thread. This thread is called a selfish thread.

 

In some situations, having selfish threads doesn't cause any problems because a higher priority thread preempts the selfish one (just as the drawing thread in the RaceApplet preempts the selfish runners). However, in other situations, threads with CPU-greedy run methods, such as the Runner class, can take over the CPU and cause other threads to wait for a long time before getting a chance to run.

Time-Slicing

Time-slicing comes into play when there are multiple "Runnable" threads of equal priority and those threads are the highest priority threads competing for the CPU.

 

The Java runtime does not implement (and therefore does not guarantee) time-slicing. However, some systems on which you can run Java do support time-slicing. Your Java programs should not rely on time-slicing as it may produce different results on different systems.

Summary

  • Most computers have only one CPU, so threads must share the CPU with other threads. The execution of multiple threads on a single CPU, in some order, is called scheduling. The Java runtime supports a very simple, deterministic scheduling algorithm known as fixed priority scheduling.

  • Each Java thread is given a numeric priority between MIN_PRIORITY and MAX_PRIORITY (constants defined in the Thread class). At any given time, when multiple threads are ready to be executed, the thread with the highest priority is chosen for execution. Only when that thread stops, or is suspended for some reason, will a lower priority thread start executing.

  • Scheduling of the CPU is fully preemptive. If a thread with a higher priority than the currently executing thread needs to execute, the higher priority thread is immediately scheduled.
  • The Java runtime will not preempt the currently running thread for another thread of the same priority. In other words, the Java runtime does not time-slice. However, the system implementation of threads underlying the Java Thread class may support time-slicing. Do not write code that relies on time-slicing.

  • In addition, a given thread may, at any time, give up its right to execute by calling the yield method. Threads can only yield the CPU to other threads of the same priority--attempts to yield to a lower priority thread are ignored.

  • When all the runnable threads in the system have the same priority, the scheduler chooses the next thread to run in a simple, non-preemptive, round-robin scheduling order.

Thread Group

The runtime system puts a thread into a thread group during thread construction. When you create a thread, you can either allow the runtime system to put the new thread in some reasonable default group or you can explicitly set the new thread's group.

The thread is a permanent member of whatever thread group it joins upon its creation--you cannot move a thread to a new group after the thread has been created.

Synchronized Threads Programming 

Monitors

In order to run a Object's method, Thread should acquire the monitor of this object.

 

A monitor is associated with a specific data item (a condition variable) and functions as a lock on that data. When a thread holds the monitor for some data item, other threads are locked out and cannot inspect or modify the data.

The code segments within a program that access the same data from within separate, concurrent threads are known as critical sections. In the Java language, you mark critical sections in your program with the synchronized keyword.


wait() , notify() and notifyAll()

Both notifyAll and wait are members of the java.lang.Object class. The notifyAll and wait methods can be invoked only by the thread that holds the lock.

 

We use wait in conjunction with notify or notifyAll to coordinate the activities of multiple threads using the same resources.

When the thread enters the wait method, the monitor is released atomically, and when the thread exits the wait method, the monitor is acquired again. This gives the waiting object the opportunity to acquire the monitor and, depending on who's waiting, resume the pending operations again.

Difference between notifyAll() and notify()

notifyAll() wakes up the thread that is waiting to get the object's monitor. 

 

notify() arbitrarily wakes up one of the threads waiting on the monitor. In this situation, each of the remaining waiting threads continues to wait until the monitor is relinquished and it is chosen by the runtime system. The use of notify can be ill-conditioned, that is, it can fail when the conditions are changed just a little. Solutions based on notifyAll tend to be more robust. A programmer who is not going to analyze a multi-threaded program sufficiently carefully is better off using notifyAll().

 

Difference between wait and sleep

Besides using these timed wait methods to synchronize threads, you can also use them in place of sleep. Both methods delay for the requested amount of time, but you can easily wake up wait with a notify. The thread can then see immediately that it should go away. This doesn't matter too much for threads that don't sleep for long, but it could be important for threads that sleep for minutes at a time.

Demo on sychronized threads programming

I put the java source code demo to help memory recovery in future.

Resource.java:

 

/**
 * @author wgu
 * 
 */
public class Resource {
	private int content = 0;
	private boolean signal = false;

	public synchronized int get() {
		while (!signal) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
		signal = false;
		notifyAll();
		return content;
	}

	public synchronized void put(int i) {
		while (signal) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
		signal = true;
		content = i;
		notifyAll();
	}
}

 

 Producer.java:

 

public class Producer extends Thread {
	int times;
	int id;
	Resource data;

	public Producer(Resource data, int times) {
		this(0, data, times);
	}

	public Producer(int id, Resource data, int times) {
		this.times = times;
		this.data = data;
		this.id = id;
	}

	public void run() {
		for (int i = 0; i < times; i++) {
			System.out.println("Producer #" + id + " put: " + i);
			data.put(i);

			try {
				// to make sure the output displayed sequentely
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
		}
	}
}

 

 Consumer.java:

 

/**
 * @author wgu
 * 
 */
public class Consumer extends Thread {
	int times;
	int id;
	Resource data;

	public Consumer(Resource data, int num) {
		this(0, data, num);
	}

	public Consumer(int id, Resource data, int num) {
		this.times = num;
		this.data = data;
		this.id = id;
	}

	public void run() {
		for (int i = 0; i < times; i++) {
			System.out.println("Consumer #" + id + " get: " + data.get());
		}
	}
}

 

 Test application, Main.java:

 

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Resource data = new Resource();
		Producer p0 = new Producer(data, 10);
		Consumer c0 = new Consumer(data, 10);

		Resource data1 = new Resource();
		Producer p1 = new Producer(1, data1, 10);
		Consumer c1 = new Consumer(1, data1, 10);

		p0.start();
		c0.start();

		p1.start();
		c1.start();
	}

}

//The output will be. Order could be different in different machine.

Producer #0 put: 0
Consumer #0 get: 0
Producer #1 put: 0
Consumer #1 get: 0
Producer #1 put: 1
Consumer #1 get: 1
Producer #0 put: 1
Consumer #0 get: 1
Producer #0 put: 2
Consumer #0 get: 2
Producer #1 put: 2
Consumer #1 get: 2
Producer #0 put: 3
Consumer #0 get: 3
Producer #1 put: 3
Consumer #1 get: 3
Producer #0 put: 4
Consumer #0 get: 4
Producer #1 put: 4
Consumer #1 get: 4
Producer #0 put: 5
Producer #1 put: 5
Consumer #0 get: 5
Consumer #1 get: 5
Producer #1 put: 6
Producer #0 put: 6
Consumer #1 get: 6
Consumer #0 get: 6
Producer #0 put: 7
Producer #1 put: 7
Consumer #1 get: 7
Consumer #0 get: 7
Producer #0 put: 8
Producer #1 put: 8
Consumer #0 get: 8
Consumer #1 get: 8
Producer #1 put: 9
Consumer #1 get: 9
Producer #0 put: 9
Consumer #0 get: 9

 

Avoid no-synchronization implementation

During synchronization implementation, we could meet the problem there is no synchronization. It could happen in many cases:

 

  • "synchronized " key word is not declared for resource's methods.
For example:
public /*synchronized*/ int get() {
		while (!signal) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
		signal = false;
		notifyAll();
		return content;
	}

public /*synchronized*/ void put(int i) {
		while (signal) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
		signal = true;
		content = i;
		notifyAll();
	}
 There will be IllegalMonitorStateException raised:
Producer #0 put: 0
Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.IllegalMonitorStateException: current thread not owner
	at java.lang.Object.notifyAll(Native Method)
	at com.gemalto.wgu.threads.test.Resource.put(Resource.java:29)
	at com.gemalto.wgu.threads.test.Producer.run(Producer.java:23)
java.lang.IllegalMonitorStateException: current thread not owner
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Unknown Source)
	at com.gemalto.wgu.threads.test.Resource.get(Resource.java:11)
	at com.gemalto.wgu.threads.test.Consumer.run(Consumer.java:22)
  • Or Object in synchronized(Object) block is not common for multiple Threads

To avoid a no-synchronization scenario, choose an object common to all relevant threads. That way, those threads compete to acquire the same object's lock, and only one thread at a time can enter the associated critical code section.

 

join()

I list here the java source code from java.lang.Thread.join(int):

 

	/**
	 * Waits at most <code>millis</code> milliseconds for this thread to die. A
	 * timeout of <code>0</code> means to wait forever.
	 * 
	 * @param millis
	 *            the time to wait in milliseconds.
	 * @exception InterruptedException
	 *                if any thread has interrupted the current thread. The
	 *                <i>interrupted status</i> of the current thread is cleared
	 *                when this exception is thrown.
	 */
	public final synchronized void join(long millis)
			throws InterruptedException {
		long base = System.currentTimeMillis();
		long now = 0;

		if (millis < 0) {
			throw new IllegalArgumentException("timeout value is negative");
		}

		if (millis == 0) {
			while (isAlive()) {
				wait(0);
			}
		} else {
			while (isAlive()) {
				long delay = millis - now;
				if (delay <= 0) {
					break;
				}
				wait(delay);
				now = System.currentTimeMillis() - base;
			}
		}
	}
 

Deadlock

JVM cannot detect or prevent deadlock. 

 

During multiple synchronized threads programming, we should provent "deadlock". For example, Both ThreadA and ThreadB needs monitros of resource1 and resource2, in case ThreadA acquire monitor of resource1 and ThreadB acquires monitor of resource2 in same time, they will be queued to waiting for the monitor release of the other.  


In order to prevent "Deadlock" issue, you have to carefully analyze source code to avoid the situations where threads might attempt to acquire each others' locks.
Here list some known programming error which could lead to deadlock:
  1. When a synchronized method calls another synchronized method.
  2. Threads cycle acquire each's resources.

Usage in My Projects

ThreadPoolExecutor from JDK 1.5

  1. ScheduledThreadPoolExecutor to launch some scheduled tasks in system level.
  2. ThreadPoolExecutor to execute some business works.

Multiple processor in Server/Request architecture

Multiple processor in socketserver side to process multiple socket client. 

This implementation comes before JDK1.5. From JDK1.5, we can use ThreadPoolExecutor to execute tasks which will process client requests.

 

Monitor Resources Status by using wait() and notifyAll()

Object.wait() and Object.notifyAll() to monitor Driver Status in DriverManager. Only after making sure all Drivers are shutdown properly, DriverManager could be cleaned.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值