Java Multi-thread Programming

Need to do a async log related task in my job, so I recently studied about Java Multi-thread programming skills. I need to blog what I learnt in the past one week, in case I would forget this knowledge in the feature. 

The thing most baffled me is how to make shared data safe, how to make them consistent while operated by multi-thread envrionment. There are serveral approaches in Java help you to do that.


1. synchronized key word

synchronized key word can be used to define a method, a field(or a property) of class, and used as synchronized block. It makes sure that any time, there would be only thread could access the protected resource.

How it could do that? That should thanks for lock mechnism. In Java, every object has a lock, either normal instance of class, or Class object itself. When a thread goes into a piece of code that decorated with synchronized, it will check if the lock is available, if no, then the thread would wait outside util it's unlock; if yes, thread gets the lock, and goes into the code.

So use synchronized to decorate different targets, different lock will be used.

1.1 Synchronized field

For example:

private synchronized ArrayList<String> data;
It will use the lock of data object, which means if a thread is operating data object, and at the same time, any other thread trying to operate it, would be all await(blocking) util the current thread end operating it.

1.2 Synchronized block

For example:

synchronized(this) {
    //operations
}
It will use the lock of this object. And the this object will be locked right after the left curly brace('{'), and unlocked right after the right curly brace('}'). Explain in this way may help you understanding, even this way is not accurate.

synchronized(this) {
    lock();
    //operations
    unlock();
}
Why I am saying this way is not accurate to display how synchronized works, because for lock()/unlock() way, if any exception or return statement happened duiring operation code, then unlock() method will be skipped. But for synchronized actually, no matter return of exception, it will always unlock the protected object.

1.3 Synchrinized Method

If synchronized used to decorate an instance method, the lock will be used is that instance on which this method invoked. If synchronized used to decorate static method, the lock will be used is the Class object of that class.

For example:

public synchronized void test() {
    //opertions
}
It makes sure at one time, only one thread is able to access this method, others should wait outside(blocking). And the place where other thread wait, is a build-in object, we can name it as wait set. As every object has a lock, every object has a wait set as well.

1.4 Synchronized method and normal method

public synchronized void write() {
}

public void read() {
}
Please consider the above sample code. A class defined two methods, one synchronized, other is not. If A thread is accessing write() method, B thread also wants to access write() method, then B should wait in wait set. But if B wants to access read() method, which is non-synchronized, it's ok to access that.

To illustrate this better, here is pic get from internet to show that:


1.5 Visiable among threads

If synchronized used to decorate a field, it gives that field another feature, visiable among threads. It means, if a thread changes value of t his field, other thread would see this changes immediately. And normail field doesn't gurantee this. I will talk about this feature in volatile section.


2. volitale key word

If a varable declared with volitale, this varialbe would be visiable among threads. The changes of this variable would be reflected to other threads immediately.

To understand this, we need introduce Java Memory Model: main memory, and work memory.


When start a thread, related variables will be copied from main memory to work memory, which is  a memory space specific for a thread. So if there is A thread and B thread, both operate on nomal variable a. Which means according to the memory model, A and B both have a copy of variable a in their own work memory. So if A changes the value a in its work memory, the value may not be seen be thread B, cause thread B still reading the old copied value in its work memory.

But if this variable is a volatile one, things would go different.

If A changes value of a, JVM will force the new value write back to main memory. And while other thread like B, trying to read varialbe A, then JVM force B get a new copy from main memory first, and do furthe operations. This is what we call visibility among threads, or memory synchronization.

Let's go back for synchronized key word, a synchronized variable also has the feature of this thread visisbility, and in the same way. So in conclusion, synchronized has two features, thread synchronization and memory synchronization; and for volitale, only has memory synchronization feature.


3. wait() and notify()/notifyAll()

wait() and notify()/notifyAll() are always used together with synchronized key word when doing multi-thread programming in Java. Acctually, wait and notify can only be used in synchronized method or synchronized block. If not, JVM will give you java.lang.IllegalMonitorStateException.

public class Queue {
	
	private String[] buffer;
	private final int SIZE = 10;
	private int count = 0;
	
	public Queue() {
		buffer = new String[SIZE];
	}
	
	public synchronized void put(String data) throws InterruptedException {
		if(count >= SIZE)
			wait();
		buffer[count++] = data;
		notifyAll();
	}
	
	public synchronized String get() throws InterruptedException {
		if(count <= 0)
			wait();
		String data = buffer[count-1];
		notifyAll();
		return data;
	}
}

If A B C threads acquired a instance of Queue, let's say queue. And A keeps invoking queue.put() method, and B C keep invoking get() method.

If queue currently empty(count = 0), so B invokes queue.get() will be await, so does C thread. So B and C are all blocked process in their own thread. Util A calls queue.put() method, and put some value inside, then invokes notifyAll(). notifyAll will notify both B and C from wait status. So one of B or C would goes into get method and get one value.


4. block and non-block code 

Use synchronized to do multi-thread programming, to protect shared resources, is a blocking way, based on pessimistic lock, which means we always deem shared resources would be inconsistent while multi threads access them at the same time.

However, the status may not be always inconsistent, and pessimistic lock would cause blocking and low efficiency. So there is another non-block synchronization approache, and this is based on infinite check.

The basic idea is, before you are trying to use a shared resource value, you should check if this value changed by other thread, if on changes, then use it, otherwise should do infinite check of this value util the old value and new value are the same.


5. cocurrent utils

java.util.cocurrent

java.util.concurrent.atomic.AtomicInteger is implemented in non-block synchronization approache.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值