Java 多线程

来源:互联网 发布:thaad知乎 编辑:程序博客网 时间:2024/05/02 23:18


实现线程的两种方式:

1. 实现java.lang.Runnable接口,实现它的run()方法。
 ①.  Place the code for the task into the run method of a class that implements the Runnable interface. That interface is very simple, with a single method:
     public interface Runnable
     {
        void run();
     }

 You simply implement a class, like this:
     class MyRunnable implements Runnable
     {
        public void run()
        {
           //task code
        }
     }
 
 ②.  Construct an object of your class:
     Runnable r = new MyRunnable();
 
 ③.  Construct a Thread object from the Runnable:
     Thread t = new Thread(r);
 
 ④.  Start the thread.
     t.start();


2. 继承java.lang.Thread,并重写它的run()方法。【这种方式不再推荐】
 class MyThread extends Thread
 {
    public void run()
    {
       //task code
    }
 }

Then you construct an object of the subclass and call its start method. However, this approach is no longer recommended. You should decouple the task that is to be run in parallel from the mechanism of running it. If you have many tasks, it is too expensive to create a separate thread for each one of them.

◆注意:
 Do not call the run method of the Thread class or the Runnable object. Calling the run method directly merely executes the task in the same threadno new thread is started. Instead, call the Thread.start method. It will create a new thread that executes the run method.


线程的状态

在Java 1.4及以下的版本中,每个线程都具有新建(NEW)、可运行(RUNNABLE)、阻塞(BLOCKED)、死亡(DEAD)四种状态,但是在Java 5.0及以上版本中,线程的状态被扩充为新建、可运行、阻塞、等待、定时等待、终止六种。线程的状态完全包含了一个线程从新建到运行,最后到结束的整个生命周期。线程状态的具体信息如下:

   ①. NEW(新建状态、初始化状态):线程对象已经被创建,但是还没有被启动时的状态。这段时间就是在我们调用new命令之后,调用start()方法之前。
   ②. RUNNABLE(可运行状态、就绪状态):在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。
   ③. BLOCKED(阻塞状态、被中断运行):线程正在等待其它的线程释放同步锁,以进入一个同步块或者同步方法继续运行;或者它已经进入了某个同步块或同步方法,在运行的过程中它调用了某个对象继承自java.lang.Object的wait()方法,正在等待重新返回这个同步块或同步方法。
   ④. WAITING(等待状态):当前线程调用了java.lang.Object.wait()、 java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三个中的任意一个方法,正在等待另外一个线程执行某个操作。比如一个线程调用了某个对象的wait()方法,正在等待其它线程调用这个对象的notify() 或者notifyAll()(这两个方法同样是继承自Object类)方法来唤醒它;或者一个线程调用了另一个线程的join()(这个方法属于 Thread类)方法,正在等待这个方法运行结束。
   ⑤. TIMED_WAITING(定时等待状态):当前线程调用了 java.lang.Object.wait(long timeout)、java.lang.Thread.join(long millis)、java.util.concurrent.locks.LockSupport.packNanos(long nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个方法中的任意一个,进入等待状态,但是与WAITING状态不同的是,它有一个最大等待时间,即使等待的条件仍然没有满足,只要到了这个时间它就会自动醒来。
   ⑥. TERMINATED(死亡状态、终止状态):线程完成执行后的状态。线程执行完run()方法中的全部代码,从该方法中退出,进入TERMINATED状态。还有一种情况是run()在运行过程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入TERMINATED状态。


线程的同步(Synchronization): synchronized和Lock

Starting with JDK 5.0, there are two mechanisms for protecting a code block from concurrent access. Earlier versions of Java used the synchronized keyword for this purpose, and JDK 5.0 introduces the ReentrantLock class.
The basic outline for protecting a code block with a ReentrantLock is:

private Lock myLock = new ReentrantLock(); // ReentrantLock implements the Lock interface
myLock.lock(); // a ReentrantLock object
try
{
   critical section
}
finally
{
   myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}

Often, a thread enters a critical section, only to discover that it can't proceed until a condition is fulfilled. You use a condition object to manage threads that have acquired a lock but cannot do useful work.
Like this:
private Lock bankLock = new ReentrantLock();
private Condition sufficientFunds = renLock.newCondition();
public void transfer(int from, int to, int amount)
{
   bankLock.lock();
   try
   {
      while (accounts[from] < amount)
         sufficientFunds.await();
      // transfer funds
      . . .
      sufficientFunds.signalAll();
   }
   finally
   {
      bankLock.unlock();
   }
}

The synchronized Keyword
In the preceding sections, you saw how to use Lock and Condition objects. Before going any further, let us summarize the key points about locks and conditions:
    ・A lock protects sections of code, allowing only one thread to execute the code at a time.
    ・A lock manages threads that are trying to enter a protected code segment.
    ・A lock can have one or more associated condition objects.
    ・Each condition object manages threads that have entered a protected code section but that cannot proceed.

Before the Lock and Condition interfaces were added to JDK 5.0, the Java language used the the synchronized keyword for concurrency jobs.
Use synchornized like this:
public synchronized void method()
{
   method body
}
Or
public void method()
{
    String lock = "LOCK";
    synchronized (lock) {
        segment
    }
}
It is legal to declare static methods as synchronized. If such a method is called, it acquires the lock of the associated class object. For example, if the Bank class has a static synchronized method, then the lock of the Bank.class object is locked when it is called.

For example, you can implement the Bank class in Java like this:

class Bank
{
   public synchronized void transfer(int from, int to, int amount) throws InterruptedException
   {
      while (accounts[from] < amount)
         wait(); // wait on object lock's single condition
      accounts[from] -= amount;
      accounts[to] += amount;
      notifyAll(); // notify all threads waiting on the condition
   }
   public synchronized double getTotalBalance() { . . . }
   private double accounts[];
}

What should you use in your codeLock and Condition objects or synchronized methods? Here is our recommendation:

    ・It is best to use neither Lock/Condition nor the synchronized keyword. In many situations, you can use one of the mechanisms of the java.util.concurrent package that do all the locking for you.
    ・If the synchronized keyword works for your situation, by all means, use it. You write less code and have less room for error.
    ・Use Lock/Condition if you specifically need the additional power that these constructs give you.

Volatile Fields


The volatile keyword offers a lock-free mechanism for synchronizing access to an instance field. If you declare a field as volatile, then the compiler and the virtual machine take into account that the field may be concurrently updated by another thread.
For example, suppose an object has a boolean flag done that is set by one thread and queried by another thread. You have two choices:

Use a lock, for example:

public synchronized boolean isDone() { return done; }
private boolean done;
(This approach has a potential drawback: the isDone method can block if another thread has locked the object.)

Declare the field as volatile:

public boolean isDone() { return done; }
private volatile boolean done;

In summary, concurrent access to a field is safe in these three conditions:

    ・The field is volatile.
    ・The field is final, and it is accessed after the constructor has completed.
    ・The field access is protected by a lock.


Read/Write Locks
The java.util.concurrent.locks package defines two lock classes, the ReentrantLock that we already discussed and the ReentrantReadWriteLock class. The latter is useful when there are many threads that read from a data structure and fewer threads that modify it. In that situation, it makes sense to allow shared access for the readers. Of course, a writer must still have exclusive access.

Here are the steps that are necessary to use read/write locks:

    1.  Construct a ReentrantReadWriteLock object:
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
    2.  Extract read and write locks:
    private Lock readLock = rwl.readLock();
    private Lock writeLock = rwl.writeLock();
    
    3.  Use the read lock in all accessors:
    public double getTotalBalance()
    {
       readLock.lock();
       try { . . . }
       finally { readLock.unlock(); }
    }
    
    4.  Use the write lock in all mutators:
    public void transfer(. . .)
    {
       writeLock.lock();
       try { . . . }
       finally { writeLock.unlock(); }
    }


死锁

Java虽然提供了Lock Test 和Timeouts策略ReentrantLock类的tryLock方法和Condition类的awaite方法等) ,但似乎还是不能避开或打破死锁。

 

附测试代码:

 

参考文献:
1. Core Java Volume II 7th Edition
2. JAVA面试题解惑系列(十)——话说多线程 http://zangweiren.javaeye.com/blog/225949

原创粉丝点击