Java 多线程与锁
多线程
线程和进程的区别
- 进程(Process)是一个正在执行程序的实例,也包括了当前使用的程序计数器、寄存器和变量的一些值。
- 线程(Thread)是一种轻量级的进程(light weight process(LWP))。线程是CPU利用的基本单元,而进程是资源分配的基本单元。
线程的优点:
创建,退出,切换都非常短暂,线程之间的通信也更简单(共享一个进程的内存和文件资源)。
线程状态(From source code):
1 public enum State { 2 /** 3 * Thread state for a thread which has not yet started. 4 */ 5 NEW, 6 7 /** 8 * Thread state for a runnable thread. A thread in the runnable 9 * state is executing in the Java virtual machine but it may 10 * be waiting for other resources from the operating system 11 * such as processor. 12 */ 13 RUNNABLE, 14 15 /** 16 * Thread state for a thread blocked waiting for a monitor lock. 17 * A thread in the blocked state is waiting for a monitor lock 18 * to enter a synchronized block/method or 19 * reenter a synchronized block/method after calling 20 * {@link Object#wait() Object.wait}. 21 */ 22 BLOCKED, 23 24 /** 25 * Thread state for a waiting thread. 26 * A thread is in the waiting state due to calling one of the 27 * following methods: 28 * <ul> 29 * <li>{@link Object#wait() Object.wait} with no timeout</li> 30 * <li>{@link #join() Thread.join} with no timeout</li> 31 * <li>{@link LockSupport#park() LockSupport.park}</li> 32 * </ul> 33 * 34 * <p>A thread in the waiting state is waiting for another thread to 35 * perform a particular action. 36 * 37 * For example, a thread that has called <tt>Object.wait()</tt> 38 * on an object is waiting for another thread to call 39 * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on 40 * that object. A thread that has called <tt>Thread.join()</tt> 41 * is waiting for a specified thread to terminate. 42 */ 43 WAITING, 44 45 /** 46 * Thread state for a waiting thread with a specified waiting time. 47 * A thread is in the timed waiting state due to calling one of 48 * the following methods with a specified positive waiting time: 49 * <ul> 50 * <li>{@link #sleep Thread.sleep}</li> 51 * <li>{@link Object#wait(long) Object.wait} with timeout</li> 52 * <li>{@link #join(long) Thread.join} with timeout</li> 53 * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 54 * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> 55 * </ul> 56 */ 57 TIMED_WAITING, 58 59 /** 60 * Thread state for a terminated thread. 61 * The thread has completed execution. 62 */ 63 TERMINATED; 64 }
创建线程
- 通过实现Runnable 接口提供一个对象 (这种方式使用实现接口而不是继承对象,耦合度更低)
1 public class HelloRunnable implements Runnable { 2 3 public void run() { 4 System.out.println("Hello from a thread!"); 5 } 6 7 public static void main(String args[]) { 8 (new Thread(new HelloRunnable())).start(); 9 } 10 11 }
When an object implementing interface Runnable
is used to create a thread, starting the thread causes the object's run
method to be called in that separately executing thread.
- 通过继承Thread类
( 代码中的 @Override注解 [覆盖], 可以不加,但是为了代码规范,建议加上。 同时编译器会检查这个注解,增强代码强健性)
1 public class HelloThread extends Thread { 2
@Override 3 public void run() { 4 System.out.println("Hello from a thread!"); 5 } 6 7 public static void main(String args[]) { 8 (new HelloThread()).start(); 9 } 10 11 }
public class Thread extends Object implements Runnable
Thread类本身实现了Runnable,但它的run()方法不做任何事情。所以可以继承Thread类,然后提供自己的run()方法实现多线程。
锁(Lock)
死锁发生的必要条件:
互斥条件(Mutual exclusion condition) 占有并等待条件( Hold and wait condition) 不可抢占条件(No preemption condition) 循环等待条件(Circular wait condition)
死锁的避免和预防
死锁demo代码
1 public class Deadlock { 2 public static void main(String[] args) { 3 final Friend alphonse = new Friend("Alphonse"); 4 final Friend gaston = new Friend("Gaston"); 5 new Thread(new Runnable() { 6 public void run() { 7 alphonse.bow(gaston); 8 } 9 }).start(); 10 new Thread(new Runnable() { 11 public void run() { 12 gaston.bow(alphonse); 13 } 14 }).start(); 15 } 16 17 static class Friend { 18 private final String name; 19 20 public Friend(String name) { 21 this.name = name; 22 } 23 24 public String getName() { 25 return this.name; 26 } 27 28 public synchronized void bow(Friend bower) { 29 System.out.format("%s: %s" 30 + " has bowed to me!%n", 31 this.name, bower.getName()); 32 bower.bowBack(this); 33 } 34 35 public synchronized void bowBack(Friend bower) { 36 System.out.format("%s: %s" 37 + " has bowed back to me!%n", 38 this.name, bower.getName()); 39 } 40 } 41 }
在这个例子中,可能出现两个线程都执行了bow()方法,但是都在等待bowBack()方法的执行,这就形成了死锁。可以形象地理解为:如果两人见面打招呼鞠躬行礼,但双方太过于客气,都必须等着对方先鞠躬完毕才回身; 一旦二人同时鞠躬时,就会一直等下去,发生死锁。
当我们在一个程序里面使用多线程的时候,可能会发生多个线程同时访问一个资源的情况,这最终可能会导致因并发问题产生不可预料的结果。
所以需要同步来让资源在某个时间段只被一个资源访问。Java中的这个实现概念叫监视器,每一个java对象都关联了一个监视器,一个线程可以加锁和解锁它。在一个监视器上,一次只能有一个线程给它加锁。
Java使用synchronized块来同步多个线程的任务,编码时应该将共享的资源保留在这个块中。
代码 Demo
1 class PrintDemo { 2 public void printCount() { 3 try { 4 for(int i = 5; i > 0; i--) { 5 System.out.println("Counter --- " + i ); 6 } 7 }catch (Exception e) { 8 System.out.println("Thread interrupted."); 9 } 10 } 11 } 12 13 class ThreadDemo extends Thread { 14 PrintDemo PD; 15 16 ThreadDemo( String name, PrintDemo pd) { 17 super(name); 18 this.PD = pd; 19 } 20 21 @Override 22 public void run() { 23 System.out.println("Thread " + this.getName() + " running..."); 24 synchronized (PD){ 25 PD.printCount(); 26 } 27 System.out.println("Thread " + this.getName() + " exiting."); 28 } 29 30 } 31 32 public class TestThread { 33 public static void main(String args[]) { 34 35 PrintDemo PD = new PrintDemo(); 36 37 ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD ); 38 ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD ); 39 40 T1.start(); 41 T2.start(); 42 43 // wait for threads to end 44 try { 45 T1.join(); // Waits for this thread to die. 46 T2.join(); 47 }catch( Exception e) { 48 System.out.println("Interrupted"); 49 } 50 } 51 }
对比与不使用synchronized的情况(输出结果可能是无序的):
1 class PrintDemo { 2 public void printCount() { 3 try { 4 for(int i = 5; i > 0; i--) { 5 System.out.println("Counter --- " + i ); 6 } 7 }catch (Exception e) { 8 System.out.println("Thread interrupted."); 9 } 10 } 11 } 12 13 class ThreadDemo extends Thread { 14 PrintDemo PD; 15 16 ThreadDemo( String name, PrintDemo pd) { 17 super(name); 18 this.PD = pd; 19 } 20 21 @Override 22 public void run() { 23 System.out.println("Thread " + this.getName() + " running..."); 24 PD.printCount(); 25 System.out.println("Thread " + this.getName() + " exiting."); 26 } 27 28 } 29 30 public class TestThread { 31 public static void main(String args[]) { 32 33 PrintDemo PD = new PrintDemo(); 34 35 ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD ); 36 ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD ); 37 38 T1.start(); 39 T2.start(); 40 41 // wait for threads to end 42 try { 43 T1.join(); 44 T2.join(); 45 }catch( Exception e) { 46 System.out.println("Interrupted"); 47 } 48 } 49 }
join() // wait for thread to die.
- 同步方法和同步代码块的区别是什么?
线程间通信(Interthread communication)
主要的方法函数:
这些方法都已在Object类中以final修饰符来实现了。并且,这三个方法只能在synchronized上下文中被调用。
Sr.No. | Method & Description |
---|---|
1 |
public void wait() Causes the current thread to wait until another thread invokes the notify(). |
2 |
public void notify() Wakes up a single thread that is waiting on this object's monitor. |
3 |
public void notifyAll() Wakes up all the threads that called wait( ) on the same object. |
1 class Chat { 2 boolean flag = false; 3 4 public synchronized void Question(String msg) { 5 if (flag) { 6 try { 7 wait(); 8 }catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 } 12 System.out.println(msg); 13 flag = true; 14 notify(); 15 } 16 17 public synchronized void Answer(String msg) { 18 if (!flag) { 19 try { 20 wait(); 21 }catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 26 System.out.println(msg); 27 flag = false; 28 notify(); 29 } 30 } 31 32 class T1 implements Runnable { 33 Chat m; 34 String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" }; 35 36 public T1(Chat m1) { 37 this.m = m1; 38 new Thread(this, "Question").start(); 39 } 40 41 public void run() { 42 for (int i = 0; i < s1.length; i++) { 43 m.Question(s1[i]); 44 } 45 } 46 } 47 48 class T2 implements Runnable { 49 Chat m; 50 String[] s2 = { "Hi", "I am good, what about you?", "Great!" }; 51 52 public T2(Chat m2) { 53 this.m = m2; 54 new Thread(this, "Answer").start(); 55 } 56 57 public void run() { 58 for (int i = 0; i < s2.length; i++) { 59 m.Answer(s2[i]); 60 } 61 } 62 } 63 public class TestThread { 64 public static void main(String[] args) { 65 Chat m = new Chat(); 66 new T1(m); 67 new T2(m); 68 } 69 }
wait()和Thread类的sleep()方法的比较:
1. 首先,wait()是object类的方法;sleep()在Thread类中。 2. Thread.sleep() 不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁 3. Thread.sleep()和Object.wait()都会暂停当前的线程。对于CPU资源来说,OS都会将执行时间分配给其它线程; 区别是,调用wait()后,需要其他线程执行notify()/notifyAll()才能够重新获得CPU执行时间。
使当前线程让出CPU使用 A hint to the scheduler that the current thread is willing to yield its current use of a processor.
code
1 public class Demo { 2 volatile static int count = 0; 3 final static int AMOUNT = 2000; 4 static int subThread = 0; 5 static int mainThread = 0; 6 7 class MyThread extends Thread{ 8 @Override 9 public void run(){ 10 while(count++ < AMOUNT){ 11 this.yield(); 12 System.out.println("Priority: " + 13 this.getPriority() + 14 "Sub thread is running!"); 15 subThread++; 16 } 17 } 18 } 19 20 public static void main(String[] args){ 21 Thread t = new Demo().new MyThread(); 22 t.start(); 23 while(count++ < AMOUNT){ 24 System.out.println("Main thread is running!"); 25 mainThread++; 26 } 27 System.out.println("sub thread run " + subThread + " times"); 28 System.out.println("main thread run " + mainThread + " times"); 29 } 30 }
Thread States - State Diagram
线程池
https://www.ibm.com/developerworks/cn/java/l-threadPool/
线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。
一般来说,线程安全的函数应该为每个调用它的线程分配专门的空间,来储存需要单独保存的状态(如果需要的话),不依赖于“线程惯性”,把多个线程共享的变量正确对待(如,通知编译器该变量为“易失(volatile)”型,阻止其进行一些不恰当的优化),而且,线程安全的函数一般不应该修改全局对象。
并发: volatile, synchronized
生产者消费者问题
相关术语: 信号灯, 互斥锁,PV语句,管程(Monitors)
代码示例: 两个生产者,三个消费者
1 import java.util.Stack; 2 3 /** 4 * Created by kev on 16-10-15. 5 */ 6 public class ProducerConsumer { 7 final static int NO_ITEMS = 10; // buffer size 8 Stack<Integer> items = new Stack<Integer>(); 9 10 public static void main(String[] args) { 11 ProducerConsumer pc = new ProducerConsumer(); 12 Producer producer = pc.new Producer(); 13 Thread t1 = new Thread(producer); 14 Thread t2 = new Thread(producer); 15 16 Consumer consumer = pc.new Consumer(); 17 Thread t3 = new Thread(consumer); 18 Thread t4 = new Thread(consumer); 19 Thread t5 = new Thread(consumer); 20 t1.start(); 21 t2.start(); 22 try { 23 Thread.sleep(100); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 28 t3.start(); 29 t4.start(); 30 t5.start(); 31 32 try { 33 t2.join(); 34 t3.join(); 35 t4.join(); 36 } catch (InterruptedException e) { 37 e.printStackTrace(); 38 } 39 } 40 41 class Producer implements Runnable { 42 43 public void produce(int i) { 44 System.out.println("Producing " + i); 45 items.push(new Integer(i)); 46 } 47 48 public void run() { 49 while (true) { 50 synchronized (items) { 51 while (items.size() >= NO_ITEMS) { 52 try { 53 items.wait(100); 54 } catch (InterruptedException e) { 55 Thread.interrupted(); 56 } 57 } 58 produce(items.size()); // 0,1,2,3... 59 items.notifyAll(); 60 } 61 62 try { 63 Thread.sleep(1000); 64 } catch (InterruptedException e) { 65 66 } 67 } 68 } 69 } 70 71 class Consumer implements Runnable { 72 73 public void consume() { 74 if (!items.isEmpty()) { 75 System.out.println("Consuming " + items.pop()); 76 } 77 } 78 79 public void run() { 80 while (true) { 81 synchronized (items) { 82 while (items.isEmpty()) { 83 try { 84 items.wait(100); 85 } catch (InterruptedException e) { 86 Thread.interrupted(); 87 } 88 } 89 consume(); 90 } 91 try { 92 Thread.sleep(3000); 93 } catch (InterruptedException e) { 94 95 } 96 } 97 } 98 } 99 }
了解系统中特定时刻有哪些线程、这些线程出于什么状态以及这些线程具体是做什么的。
- JDK自带的jvisualvm可以实现线程的监视,它适合在开发和测试环境下监视Java系统中的线程情况。
- 如果是线上环境,可以使用JDK自带的另外一个工具jstack,通过它可以获取指定Java进程的线程信息。
- 在java 8 中,我们还可以使用Java Mission Control(JMC)这个工具来监视java线程。
以上程序直接在终端输入便可运行($JAVA_HOME/bin/目录下)。
Java Concurrency 包 (java.util.concurrent)
Executor, 接口类,只提供了一个execute方法
An object that executes submitted Runnable
tasks.
ExecutorService, 接口类,继承自Executor
An {@link Executor} that provides methods to manage termination and
* methods that can produce a {@link Future} for tracking progress of
* one or more asynchronous tasks.
ThreadPoolExecutor 继承自AbstractExecutorService (提供了ExecutorService的部分默认实现)
An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
* using {@link Executors} factory methods.
http://www.tutorialspoint.com/java/java_multithreading.htm
https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
http://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
https://zh.wikipedia.org/wiki/%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98