Java 多线程与锁

多线程

线程和进程的区别

  • 进程(Process)是一个正在执行程序的实例,也包括了当前使用的程序计数器、寄存器和变量的一些值。
  • 线程(Thread)是一种轻量级的进程(light weight process(LWP))。线程是CPU利用的基本单元,而进程是资源分配的基本单元。

线程的优点:

  创建,退出,切换都非常短暂,线程之间的通信也更简单(共享一个进程的内存和文件资源)。

 

java.lang.Thread

《Java 多线程与锁》

线程状态(From source code):

《Java 多线程与锁》
《Java 多线程与锁》

 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     }

View Code

 

 

 

创建线程

  • 通过实现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 }

java.lang.Thread

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代码

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

  在这个例子中,可能出现两个线程都执行了bow()方法,但是都在等待bowBack()方法的执行,这就形成了死锁。可以形象地理解为:如果两人见面打招呼鞠躬行礼,但双方太过于客气,都必须等着对方先鞠躬完毕才回身; 一旦二人同时鞠躬时,就会一直等下去,发生死锁。

 

同步(Synchronization)

当我们在一个程序里面使用多线程的时候,可能会发生多个线程同时访问一个资源的情况,这最终可能会导致因并发问题产生不可预料的结果。

所以需要同步来让资源在某个时间段只被一个资源访问。Java中的这个实现概念叫监视器,每一个java对象都关联了一个监视器,一个线程可以加锁和解锁它。在一个监视器上,一次只能有一个线程给它加锁。
Java使用synchronized块来同步多个线程的任务,编码时应该将共享的资源保留在这个块中。

代码 Demo

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

对比与不使用synchronized的情况(输出结果可能是无序的):

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

join()   //  wait for thread to die.

 

  •  同步方法和同步代码块的区别是什么?

    同步方法默认用this或者当前类class对象作为锁;     同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;  

线程间通信(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.

代码Demo:

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

 wait()和Thread类的sleep()方法的比较:

1. 首先,wait()是object类的方法;sleep()在Thread类中。
2. Thread.sleep() 不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁
3. Thread.sleep()和Object.wait()都会暂停当前的线程。对于CPU资源来说,OS都会将执行时间分配给其它线程;
    区别是,调用wait()后,需要其他线程执行notify()/notifyAll()才能够重新获得CPU执行时间。

  

yield()

使当前线程让出CPU使用 A hint to the scheduler that the current thread is willing to yield its current use of a processor.

code

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

 

《Java 多线程与锁》

Thread States – State Diagram

 

 

线程池

  https://www.ibm.com/developerworks/cn/java/l-threadPool/

 

线程安全编程中的术语,指某个函数函数库多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

一般来说,线程安全的函数应该为每个调用它的线程分配专门的空间,来储存需要单独保存的状态(如果需要的话),不依赖于“线程惯性”,把多个线程共享的变量正确对待(如,通知编译器该变量为“易失(volatile)”型,阻止其进行一些不恰当的优化),而且,线程安全的函数一般不应该修改全局对象。

并发: volatile, synchronized

 

生产者消费者问题

wikipedia 

相关术语: 信号灯, 互斥锁,PV语句,管程(Monitors) 

代码示例: 两个生产者,三个消费者

《Java 多线程与锁》
《Java 多线程与锁》

 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 }

View Code

 

线程的监视

了解系统中特定时刻有哪些线程、这些线程出于什么状态以及这些线程具体是做什么的。

  • 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

    原文作者:artificerpi
    原文地址: https://www.cnblogs.com/7explore-share/p/5925271.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞