进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,即进程空间或(虚空间)。进程不依赖于线程而独立存在,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,线程没有自己的虚拟地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。
区别:
线程 | 进程 | |
---|---|---|
地址空间 | 共享本进程的地址空间 | 进程之间是独立的空间 |
资源 | 共享本进程的资源(内存,I/O,CPU等) | 进程之间的资源是独立的 |
执行过程 | 线程不能独立执行,必须存在于应用程序中,执行开销小 | 每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口、执行开销大 |
并发性 | 可并发执行 | 可并发执行 |
调度和分配的基本单位 | 拥有资源的基本单位 |
Java中创建线程的两种方式
继承Thread类并重写run()方法
/** * 测试扩展Thread类实现的多线程程序 */ public class TestThread extends Thread { public TestThread(String name){ super(name); } @Override public void run() { for(int i=0;i<5;i++){ for(long k=0;k<100000000;k++); System.out.println(this.getName()+":"+i); } } public static void main(String[] args){ Thread t1=new TestThread("李白"); Thread t2=new TestThread("屈原"); t1.start(); t2.start(); } }
通过实现Runnable接口创建
/** * 实现Runnable接口的类 */ public class RunnableImpl implements Runnable{ private Stringname; public RunnableImpl(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 5; i++) { for(long k=0;k<100000000;k++); System.out.println(name+":"+i); } } } /** * 测试Runnable类实现的多线程程序 */ public class TestRunnable { public static void main(String[] args) { RunnableImpl ri1=new RunnableImpl("李白"); RunnableImpl ri2=new RunnableImpl("屈原"); Thread t1=new Thread(ri1); Thread t2=new Thread(ri2); t1.start(); t2.start(); } }
线程离开运行态:
锁的原理:
Java每个对象都有一个内置锁,当程序运行到非静态的synchronized()方法/synchronized()代码块上时,自动获得当前实例(this)的锁,一个对象只有一个锁,所以当一个线程获得锁之后,其他线程都不能进入该实例的synchronized()方法/synchronized()代码块,直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法/代码块。
note:
静态方法同步:需要一个应用于整个类对象的锁
public staticsynchronized int setName(String name){ Xxx.name = name; } //等价 public static intsetName(String name){ synchronized(Xxx.class){ Xxx.name = name; } }
调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
void notify()——唤醒在此对象监视器上等待的单个线程。 void notifyAll()——唤醒在此对象监视器上等待的所有线程。 void wait()——导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。
休眠
Thread.sleep(long millis) Thread.sleep(long millis, int nanos)
线程休眠,会将CPU资源交给其他线程,但不会释放锁,以便线程可以轮换执行,休眠一定时间后,会再次进入就绪态
优先级
Thread.setPriority(10);
线程的优先级无法保证线程的执行顺序,但是优先级较高的线程获取资源的概率较大,线程优先级默认为5
在父线程中开启子线程,子线程的优先级会继承父线程的优先级
让步
Thread.yield()
线程让步的意思是当前线程暂停执行,让出CPU资源,并执行其他线程,但并不能指定是哪个线程,yield()是静态方法
合并
void join()——等待该线程终止。 void join(longmillis)——等待该线程终止的时间最长为 millis毫秒。 void join(longmillis,int nanos)——等待该线程终止的时间最长为 millis毫秒 + nanos 纳秒。
将几个并行的线程合并为一个单线程执行,当前线程会暂停执行,知道新加入的线程执行完毕
守护线程
public final void setDaemon(boolean on)--将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。
调用该方法可以将线程设置为守护线程,必须在启动前调用
JVM的垃圾回收,内存管理,操作系统的线程很多都是守护线程
JRE判断程序是否执行完毕的标准是所有的前台线程执行完毕,而不管后台线程的状态
同步方法
synchronized 关键词修饰方法
synchronized只能标记非抽象的方法,不能标识成员变量。
同步块
synchronized 关键字修饰代码块
在同步方法或同步代码块中应该尽可能减少使用sleep()和yield(),使用sleep()的话,该方法占着对象锁,其他线程无法访问该资源,使用yield()时,其他线程也无法访问同步方法或者同步代码块。
生产者消费者模型
死锁
发生死锁的原因:两个对象的锁互相等待
volatile关键字
volatile可以用在变量前面,防止两个线程同时操作数据引起其中一个线程读取到脏数据的情况
volatile的同步性比较差,但是其开销较低
线程池
线程池:在一块内存空间内,存放未死亡的线程,线程调度由池管理器管理。可以将常用的一些线程存放在线程池中,当有线程任务时,直接在线程池中取出,执行完成后放回线程池,可以节省反复创建相同线程的时间和空间开销,节省资源。
固定大小的线程池
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Java线程:线程池 */ public class Test { public static void main(String[] args) { //创建一个可重用固定线程数的线程池 ExecutorService pool =Executors.newFixedThreadPool(2); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 Thread t1 = new MyThread(); Thread t2 = new MyThread(); //将线程放入线程池中进行执行 pool.execute(t1); pool.execute(t2); //关闭线程池 pool.shutdown(); } } class MyThread extends Thread{ public void run(){ System.out.println(Thread.currentThread().getName()+"正在执行..."); } }
单任务线程池
ExecutorService pool=Executors.newSingleThreadExecutor();
单任务线程池中只有一个线程,当需要反复执行一个线程时,避免重复新建该线程,可以用线程池实现
可变大小的线程池
ExecutorService pool=Executors.newCachedThreadPool();
可以根据需要改变大小的线程池
延迟连接池
在给定延迟后运行命令或者定期地执行
ScheduledExecutorServicepool=Executors.newScheduledThreadPool(2); pool.schedule(thread, 10, TimeUnit.MILLISECONDS);
创建一个单任务执行线程池,它可安排在给定延迟后运行命令或者定期地执行
ScheduledExecutorServicepool=Executors.newSingleThreadScheduledExecutor();
自定义线程池
/* * corePoolSize : 池中所保存的线程数,包括空闲线程 * maximumPoolSize : 池中允许的最大线程数 * keepAliveTime : 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间 * unit -keepAliveTime : 参数的时间单位 * workQueue : 执行前用于保持任务的队列 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)