一、并发编程入门 1.1 线程 1.1.1进程和线程进程 进程 :
进程指正在运行的程序,进程拥有一个完整的、私有的基本运行资源集合。通常,每个进程都有
自己的内存空间。
进程往往被看作是程序或应用的代名词,然而,用户看到的一个单独的应用程序实际上可能是一组相互 协作的进程集合。
为了便于进程之间的通信,大多数操作系统都支持进程间通信(IPC),如pipes 和sockets。IPC不仅支持同一系统上的通信,也支持不同的系统。IPC通信方式包括管道(包括无名管道和命名管道)、 消息队列、信号量、共享存储、Socket、Streams等方式,其中 Socket和Streams支持不同主机上的两个进程IPC。
线程 :
线程有时也被称为轻量级的进程。进程和线程都提供了一个执行环境,但创建一个新的线程比创建一个新的进程需要的资源要少。
线程是在进程中存在的 — 每个进程最少有一个线程。线程共享进程的资源,包括内存和打开的文件。这样提高了效率,但潜在的问题就是线程间的通信。
多线程的执行是Java平台的一个基本特征。每个应用都至少有一个线程 – 或几个,如果算上“系
统”线程的话,比如内存管理和信号处理等。但是从程序员的角度来看,启动的只有一个线程,叫主线 程。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
1.1.2 线程实践 1.1.2.1 线程的创建 两种方式:
1 2 3 4 5 6 7 8 public class HelloThread extends Thread { public void run () { System.out.println("Hello from a thread!" ); } public static void main (String args[]) { (new HelloThread()).start(); } }
1 2 3 4 5 6 7 8 public class HelloRunnable implements Runnable { public void run () { System.out.println("Hello from a thread!" ); } public static void main (String args[]) { (new Thread(new HelloRunnable())).start(); } }
1.1.2.2 线程启动和停止 启动线程 :调用start方法
停止线程 :线程自带的stop方法,一方面已经过时,另一方面,不会对停止的线程做状态保存,使得线程中涉及的 对象处于未知状态,如果这些状态,其他线程也会使用,将会使得其他线程出现无法预料的异常,所 以,停止程序的功能,需要自己实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ThreadTest { public static void main (String[] args) throws InterruptedException { StopThread thread = new StopThread(); thread.start(); Thread.sleep(1000L ); thread.stop(); while (thread.isAlive()) {} thread.print(); } private static class StopThread extends Thread { private int x = 0 ; private int y = 0 ; @Override public void run () { synchronized (this ) { ++x; try { Thread.sleep(3000L ); } catch (InterruptedException e) { e.printStackTrace(); } ++y; } } public void print () { System.out.println("x=" + x + " y=" + y); } } }
上述代码中,run方法里是一个同步的原子操作,x和y必须要共同增加,然而这里如果调用 thread.stop()方法强制中断线程,输出如下:
没有异常,也破坏了我们的预期。如果这种问题出现在我们的程序中,会引发难以预期的异常。因此这 种不安全的方式很早就被废弃了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyRunnable implements Runnable { private boolean doStop = false ; public synchronized void doStop () { this .doStop = true ; } private synchronized boolean keepRunning () { return this .doStop == false ; } @Override public void run () { while (keepRunning()) { System.out.println("Running" ); try { Thread.sleep(3L * 1000L ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
调用
1 2 3 4 5 6 7 8 9 10 11 12 13 public class MyRunnableMain { public static void main (String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); try { Thread.sleep(10L * 1000L ); } catch (InterruptedException e) { e.printStackTrace(); } myRunnable.doStop(); } }
1.1.2.3 线程暂停和中断 暂停 : Java中线程的暂停是调用 java.lang.Thread 类的 sleep 方法。该方法会使当前正在执行的线程暂停 指定的一段时间,如果线程持有锁, sleep 方法结束前并不会释放该锁。中断 : java.lang.Thread类有一个 interrupt 方法,该方法直接对线程调用。当被interrupt的线程正在 sleep或wait时,会抛出 InterruptedException 异常。 事实上, interrupt 方法只是改变目标线程的中断状态(interrupt status),而那些会抛出 InterruptedException 异常的方法,如wait、sleep、join等,都是在方法内部不断地检查中断状 态的值。
interrupt方法 Thread实例方法:必须由其它线程获取被调用线程的实例后,进行调用。实际上,只是改变了被调用线 程的内部中断状态;
Thread.interrupted方法 Thread类方法:必须在当前执行线程内调用,该方法返回当前线程的内部中断状态,然后清除中断状态 (置为false) ;
isInterrupted方法 Thread实例方法:用来检查指定线程的中断状态。当线程为中断状态时,会返回true;否则返回 false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ThreadTest { public static void main (String[] args) throws InterruptedException { StopThread thread = new StopThread(); thread.start(); Thread.sleep(1000L ); thread.interrupt(); while (thread.isAlive()) { } thread.print(); } private static class StopThread extends Thread { private int x = 0 ; private int y = 0 ; @Override public void run () { synchronized (this ) { ++x; try { Thread.sleep(3000L ); } catch (InterruptedException e) { e.printStackTrace(); } ++y; } } public void print () { System.out.println("x=" + x + " y=" + y); } } }
输出结果如下:
1 2 3 4 x=1 y=1 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at ThreadTest$StopThread.run(ThreadTest.java:28 )
x=1,y=1 这个结果是符合我们的预期,同时还抛出了个异常。
底层源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public void interrupt () { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null ) { interrupt0(); b.interrupt(this ); return ; } } interrupt0(); } public static boolean interrupted () { return currentThread().isInterrupted(true ); } public boolean isInterrupted () { return isInterrupted(false ); } private native boolean isInterrupted (boolean ClearInterrupted) ;
interrupt() :
interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个 InterruptedException异常;
如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
如果线程在Selector上被阻塞,select方法将立即返回;
如果非以上情况,将直接标记 interrupt 状态;
注意:interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的 线程,不会受 interrupt 中断; 阻塞情况下中断,抛出异常后线程恢复非中断状态,即 interrupted = false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ThreadTest { public static void main (String[] args) throws InterruptedException { Thread t = new Thread(new Task("mytask" )); t.start(); t.interrupt(); } static class Task implements Runnable { String name; public Task (String name) { this .name = name; } @Override public void run () { try { Thread.sleep(1000 ); } catch (InterruptedException e) { System.out.println("thread has been interrupt!" ); } System.out.println("isInterrupted: " + Thread.currentThread().isInterrupted()); System.out.println("task " + name + " is over" ); } } }
输出:
1 2 3 thread has been interrupt! isInterrupted: false task 1 is over
调用Thread.interrupted() 方法后线程恢复非中断状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class ThreadTest { public static void main (String[] args) throws InterruptedException { Thread t = new Thread(new Task("mytask" )); t.start(); t.interrupt(); } static class Task implements Runnable { String name; public Task (String name) { this .name = name; } @Override public void run () { System.out.println("first :" + Thread.interrupted()); System.out.println("second:" + Thread.interrupted()); System.out.println("task " + name + " is over" ); } } }
输出结果:
1 2 3 first :true second:false task 1 is o
1.1.2.4 线程的状态 Java线程可能的状态:
1.2 多线程 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
1.2.1 并发和并行
并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群
1.2.2 多线程好处 提高cpu的利用率
单线程:
1 2 3 4 5 6 5 seconds reading file A 2 seconds processing file A 5 seconds reading file B 2 seconds processing file B ---------------------- 14 seconds total
多线程:
1 2 3 4 5 6 5 seconds reading file A 5 seconds reading file B + 2 seconds processing file A 2 seconds processing file B ---------------------- 12 seconds total
一般来说,在等待磁盘IO,网络IO或者等待用户输入时,CPU可以同时去处理其他任务。
更高效的响应
多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态,不会 造成无法响应的现象。
公平使用CPU资源
当前没有进行处理的任务,可以将处理器时间让给其它任务;占用大量处理时间的任务,也可以定期将 处理器时间让给其它任务;通过对CPU时间的划分,使得CPU时间片可以在多个线程之间切换,避免需要 长时间处理的线程独占CPU,导致其它线程长时间等待。
1.2.3 多线程的代价 *更复杂的设计 *
共享数据的读取,数据的安全性,线程之间的交互,线程的同步等;
上下文环境切换
线程切换,cpu需要保存本地数据、程序指针等内容;
更多的资源消耗
每个线程都需要内存维护自己的本地栈信息,操作系统也需要资源对线程进行管理维护;