标签:
当一个程序进入内存运行时,即变成一个进程,进程特征:独立性;动态性;并发性。线程也被称为轻量级进程,线程是进程的执行单元,线程在程序中是独立的、并发的执行流,线程可以拥有自己的堆栈、程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。线程的执行是抢占式的,线程比进程拥有更高的性能。
1)区别并发性和并行性:
并发性:同一时刻只有一条指令执行,多个进程指令被快速轮换执行。
并行性:在同一时刻有多条指令在多个处理器上同时执行。
2)使用多线程编程的好处:
进程之间不能共享内存,但线程之间可以;使用多线程来实现多任务并发比多进程的效率高;Java内置了多线程功能支持。
1)通过继承Thread类来创建并启动多线程
public class MyThread extends Thread { private int i ; // 重写run方法,run方法的方法体就是线程执行体 public void run() { for ( ; i < 100 ; i++ ) { // 当线程类继承Thread类时,直接使用this即可获取当前线程 // Thread对象的getName()返回当前该线程的名字 System.out.println(getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { // 调用Thread的currentThread方法获取当前线程 System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { new MyThread().start(); // 创建并启动第一条线程 new MyThread().start(); // 创建并启动第二条线程 } } } }
public class MyThread2 implements Runnable { private int i ; // run方法同样是线程执行体 public void run() { for ( ; i < 100 ; i++ ) { // 当线程类实现Runnable接口时,如果想获取当前线程,只能用Thread.currentThread()方法 System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { MyThread2 st = new MyThread2(); // 通过new Thread(target , name)方法创建新线程 new Thread(st , "新线程1").start(); new Thread(st , "新线程2").start(); } } } }
public class MyThread3 { public static void main(String[] args) { // 创建Callable对象 MyThread3 rt = new MyThread3(); // 先使用Lambda表达式创建Callable<Integer>对象 // 使用FutureTask来包装Callable对象 FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> { int i = 0; for ( ; i < 100 ; i++ ) { System.out.println(Thread.currentThread().getName() + "的循环变量i的值:" + i); } // call()方法可以有返回值 return i; }); for (int i = 0 ; i < 100 ; i++) { System.out.println(Thread.currentThread().getName() + "的循环变量i的值:" + i); if (i == 20) { // 实质还是以Callable对象来创建、并启动线程 new Thread(task , "有返回值的线程").start(); } } try { // 获取线程返回值 System.out.println("子线程的返回值:" + task.get()); } catch (Exception ex) { ex.printStackTrace(); } } }
注意:启动线程的时候如果直接调用run方法,则run方法立即执行,而且在run方法返回之前其他线程无法并发执行,变成了单线程程序。
1)继承Thread类来创建多线程
优点:编写简单,访问当前线程简单,直接使用this即可。
缺点:不能继承其他父类
2)实现Runnable接口或Callable接口来创建多线程
优点:共享一个target对象,适合多个线程来处理一个资源的情况。
缺点:编程相对复杂,访问当前线程必须使用Thread.currentThread()方法。
故一般推荐采用实现Runnable接口或Callable接口来创建多线程
synchronized (obj) { // obj:同步监视器,线程开始执行同步代码块之前,必须先获得对同步监视器的锁定 // 同步代码块 }通常推荐使用可能被并发访问的共享资源充当同步监视器
public class DrawThread extends Thread { ... public void run() { // 使用account作为同步监视器,任何线程进入下面同步代码块之前, // 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改它 // 这种做法符合:“加锁 → 修改 → 释放锁”的逻辑 synchronized (obj) { ... } // 同步代码块结束,该线程释放同步锁 } ... }
2)同步方法
同步方法就是使用synchronized关键字来修饰某个方法(synchronized关键字可以修改方法、代码块,但是不能修饰构造器、成员变量)。
public class Account { ... // 提供一个线程安全draw()方法来完成操作 public synchronized void draw(double drawAmount) { ... } ... }拓展:
可变类的线程安全是以降低程序的运行效率作为代价的,为了减少线程安全所带来的负面影响,程序可以采用如下策略:
a)不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法同步。
b)如果可变类有两种运行环境(单线程和多线程环境),则应该为该可变类提供线程不安全版本(单线程环境中使用线程不安全版本以保证性能)和线程安全版本。
3)释放同步监视器的锁定
a)线程会在如下几种情况释放对同步监视器的锁定:
同步方法、同步代码块执行结束;
同步方法、同步代码块中遇到break、return终止了执行;
出现了未处理的Error或Exception;
执行了同步监视器的wait()方法,则当前线程暂停并释放同步监视器。
b)如下几种情况线程不会释放对同步监视器的锁定:
调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行;
其他线程调用了该线程的suspend()方法将该线程挂起。
4)同步锁(Lock)
Lock(Java5新特性)提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的Condition对象。
5)死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁
标签:
原文地址:http://blog.csdn.net/smartbetter/article/details/51335165