Deadlock

Deadlock 死锁

Too much synchronization can be problematic. If you are not careful, you might encounter a situation
where locks are acquired by multiple threads, neither thread holds its own lock but holds the lock
needed by some other thread,
and neither thread can enter and later exit its critical section to
release its held lock because some other thread holds the lock to that critical section. Listing 7-25’s
atypical example demonstrates this scenario, which is known as deadlock.

太多同步可能是有问题的,如果你不小心,你可能会碰到锁被多个线程取得的情况,线程都没有持有自己的锁但持有其他某个线程需要的锁,没有线程能够进入并在后来退出它的临界段以释放它的持有锁,因为某个其它的线程持有那个临界段的锁。

Listing 7-25. 病态的死锁

public class DeadlockDemo
{
private Object lock1 = new Object();
private Object lock2 = new Object();


public void instanceMethod1()
{
synchronized(lock1)
{
synchronized(lock2)
{
System.out.println("first thread in instanceMethod1");
// critical section guarded first by
// lock1 and then by lock2
}
}
}


public void instanceMethod2()
{
synchronized(lock2)
{
synchronized(lock1)
{
System.out.println("second thread in instanceMethod2");

// critical section guarded first by
// lock2 and then by lock1
}
}
}


public static void main(String[] args)
{
final DeadlockDemo dld = new DeadlockDemo();

Runnable r1 = new Runnable()
{
@Override
public void run()
{
while(true)
{
dld.instanceMethod1();
try
{
Thread.sleep(50);
}
catch (InterruptedException ie)
{
}
}
}
};
Thread thdA = new Thread(r1);


Runnable r2 = new Runnable()
{
@Override
public void run()
{
while(true)
{
dld.instanceMethod2();
try
{
Thread.sleep(50);
}
catch (InterruptedException ie)
{
}
}
}
};
Thread thdB = new Thread(r2);
thdA.start();
thdB.start();
}
}

Listing 7-25’s thread A and thread B call instanceMethod1() and instanceMethod2(), respectively,
at different times.  Consider the following execution sequence:
1.  Thread A calls instanceMethod1(), obtains the lock assigned to the lock1-
referenced object, and enters its outer critical section (but has not yet
acquired the lock assigned to the lock2-referenced object).

考虑下列执行顺序:

1.线程A调用 instanceMethod1(),获得分配给lock1引用的对象的锁,并进入它的外部临界段(但还没有获得赋予lock2引用的对象的锁。)

2.  Thread B calls instanceMethod2(), obtains the lock assigned to the lock2-
referenced object, and enters its outer critical section (but has not yet
acquired the lock assigned to the lock1-referenced object).


3.  Thread A attempts to acquire the lock associated with lock2. The virtual
machine forces the thread to wait outside of the inner critical section
because thread B holds that lock.


4.  Thread B attempts to acquire the lock associated with lock1. The virtual
machine forces the thread to wait outside of the inner critical section
because thread A holds that lock.
5.  Neither thread can proceed because the other thread holds the needed lock.
You have a deadlock situation and the program (at least in the context of the
two threads) freezes up.


Although the previous example clearly identifies a deadlock state, it’s often not that easy to detect
deadlock. For example, your code might contain the following circular relationship among various
classes (in several source files):
Class A’s synchronized method calls class B’s synchronized method. 
Class B’s synchronized method calls class C’s synchronized method.  
Class C’s synchronized method calls class A’s synchronized method.  


If thread A calls class A’s synchronized method and thread B calls class C’s synchronized method,
thread B will block when it attempts to call class A’s synchronized method and thread A is still inside
of that method. Thread A will continue to execute until it calls class C’s synchronized method, and
then block. Deadlock is the result.

Note Neither the Java language nor the virtual machine provides a way to prevent deadlock, and so the
burden falls on you. The simplest way to prevent deadlock from happening is to avoid having either a
synchronized method or a synchronized block call another synchronized method/block. Although this advice
prevents deadlock from happening, it is impractical because one of your synchronized methods/blocks
might need to call a synchronized method in a Java API, and the advice is overkill because the synchronized
method/block being called might not call any other synchronized method/block, so deadlock would not occur.

Thread-Local Variables

 Tread-Local变量


You will sometimes want to associate per-thread data (such a user ID) with a thread. Although you
can accomplish this task with a local variable, you can only do so while the local variable exists. You
could use an instance field to keep this data around longer, but then you would have to deal with
synchronization. Thankfully, Java supplies ThreadLocal as a simple (and very handy) alternative.
Each instance of the ThreadLocal class describes a thread-local variable, which is a variable that
provides a separate storage slot to each thread that accesses the variable. You can think of a
thread-local variable as a multislot variable in which each thread can store a different value in the
same variable. Each thread sees only its value and is unaware of other threads having their own
values in this variable.

你有时会想关联每线程数据(如用户ID)和线程。尽管你可以用一个局部变量完成这项任务,当局部变量存在时你只能这样做。

你可以使用实例字段保持这个数据更久,但是你然后需要处理同步。

幸运的是,Java供应ThreadLocal作为一个简单的(非常有用)的选择。 

ThreadLocal类的每个实例描述了一个线程局部变量,这是一个变量,给访问这个变量的各个线程提供了一个单独的存储槽

你可以把线程局部变量想成是多槽变量-每个线程可以在相同的变量中存储不同的值。

每个线程只能看到它自己的值也不知道在这个变量中其它线程有它们自己的值。

ThreadLocal is generically declared as ThreadLocal<T>, where T identifies the type of value that is
stored in the variable. This class declares the following constructor and methods:


 ThreadLocal() creates a new thread-local variable.
 T get() returns the value in the calling thread’s storage slot. If an entry doesn’t
exist when the thread calls this method, get() calls initialValue().


 T initialValue() creates the calling thread’s storage slot and stores an initial
(default) value in this slot. The initial value defaults to null. You must subclass
ThreadLocal and override this protected method to provide a more suitable
initial value.
 void remove() removes the calling thread’s storage slot. If this method is
followed by get() with no intervening set(), get() calls initialValue().

 void remove() 移除调用线程的存储槽。如何这个方法后跟 get() 而没有set()干预,get() 调用s initialValue()

 void set(T value) sets the value of the calling thread’s storage slot to value.


Listing 7-26 shows how to use ThreadLocal to associate different user IDs with two threads.

清单7-26 表明如何使用ThreadLocal来联系不同的用户ID和两个线程。


Listing 7-26. Different User IDs for Different Threads

清单7-26. 不同线程的不同用户ID


public class ThreadLocalDemo
{
private static volatile ThreadLocal<String> userID =new ThreadLocal<String>();
public static void main(String[] args)
{

Runnable r = new Runnable() 
{
@Override
public void run()
{
String name = Thread.currentThread().getName();
if (name.equals("A"))
    userID.set("foxtrot");
else
    userID.set("charlie");
    System.out.println(name + " " + userID.get());
}
};

Thread thdA = new Thread(r);
thdA.setName("A");
Thread thdB = new Thread(r);
thdB.setName("B");
thdA.start();
thdB.start();

}
}


After instantiating ThreadLocal and assigning the reference to a volatile class field named
userID (the field is volatile because it is accessed by different threads, which might execute on
a multiprocessor/multicore machine), the default main thread creates two more threads that store
different String objects in userID and output their objects.


When you run this application, you will observe the following output (possibly not in this order):
A foxtrot
B charlie


Values stored in thread-local variables are not related. When a new thread is created, it gets a new
storage slot containing initialValue()’s value. Perhaps you would prefer to pass a value from a
parent thread, a thread that creates another thread, to a child thread, the created thread. You
accomplish this task with InheritableThreadLocal.


InheritableThreadLocal is a subclass of ThreadLocal. As well as declaring an
InheritableThreadLocal() constructor, this class declares the following protected method:

InheritableThreadLocal 是ThreadLocal的子类。除了声明ThreadLocal()构造器外,该类也声明下列授保护的方法:


 T childValue(T parentValue) calculates the child’s initial value as a function of
the parent’s value at the time the child thread is created. This method is called
from the parent thread before the child thread is started. The method returns the
argument passed to parentValue and should be overridden when another value
is desired.

在子线程被创建的时候,根据父的值计算子的初始值。在子线程被启动之前,从父线程调用这个方法。


Listing 7-27 shows how to use InheritableThreadLocal to pass a parent thread’s Integer object to
a child thread.
Listing 7-27. Passing an Object from Parent Thread to Child Thread


public class InheritableThreadLocalDemo
{
    private static volatile InheritableThreadLocal<Integer> intVal =new InheritableThreadLocal<Integer>();
    public static void main(String[] args)
   {
   Runnable rP = new Runnable()
   {
   @Override
    public void run()
    {
    intVal.set(new Integer(10));
    Runnable rC = new Runnable()

{
@Override
public void run()
{
Thread thd;
thd = Thread.currentThread();
String name = thd.getName();
System.out.println(name + " " +
intVal.get());
}
};
Thread thdChild = new Thread(rC);
thdChild.setName("Child");
thdChild.start();
}
};
new Thread(rP).start();
}
}

After instantiating InheritableThreadLocal and assigning it to a volatile class field named intVal,
the default main thread creates a parent thread, which stores an Integer object containing 10 in
intVal. The parent thread creates a child thread, which accesses intVal and retrieves its parent
thread’s Integer object.
When you run this application, you will observe the following output:
Child 10

Note For more insight into ThreadLocal and how it is implemented, check out Patson
Luk’s “A Painless Introduction to Java's ThreadLocal Storage” blog post (http://java.
dzone.com/articles/painless-introduction-javas-threadlocal-storage).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值