目录
一.线程与进程
1.线程与进程
程序(program):
是为了完成特定任务、用某种语言编写的一组指令的集合,是一段静态的代码。(程序是静态的)
进程(process):
是程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。
进程是动态的,是一个动的过程,有它自身的产生、存在和消亡的过程。
线程(thread):
进程可以进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程。
2.线程调度
目的是为了更合理的利用CPU
分时调度:
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度(Java使用的调度方式):
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
但其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
3、Java中的线程:特征和状态
1. 所有的Java 程序,不论并发与否,都有一个名为主线程的Thread 对象。执行该程序时, Java虚拟机( JVM )将创建一个新Thread 并在该线程中执行main()方法。这是非并发应用程
序中唯一的线程,也是并发应用程序中的第一个线程。
2. Java中的线程共享应用程序中的所有资源,包括内存和打开的文件,快速而简单地共享信息。但是必须使用同步避免数据竞争。
3. Java中的所有线程都有一个优先级,这个整数值介于Thread.MIN_PRIORITY(1)和Thread.MAX_PRIORITY(10)之间,默认优先级是Thread.NORM_PRIORITY(5)。线程的
执行顺序并没有保证,通常,较高优先级的线程将在较低优先级的钱程之前执行。
4. 在Java 中,可以创建两种线程:
- 守护线程。
- 非守护线程。
区别在于它们如何影响程序的结束。
所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
4、线程的六种状态
Thread类有一个State枚举类,
一个线程的状态。线程可以处于以下状态之一:
在给定的时间点,线程只能处于一种状态。这些状态是虚拟机状态,不反应任何操作系统状态。
1、NEW
线程创建之后,尚未启动的线程的线程状态。
Thread thread = new Thread();
System.out.println(thread.getState()); // NEW
2、RUNNABLE
在Java虚拟机中正在运行的线程处于此状态。在RUNNABLE状态下的线程可能会处于等待状态, 因为它正在等待一些系统资源的释放,比如IO.
3、BLOCKED
被阻塞,等待锁的线程处于此状态。
比如线程A进入了一个synchronized方法,线程B也想进入这个方法,但是这个方法的锁已经被线程A获取了,这个时候线程B就处于BLOCKED状态。
public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " invoke");
try {
Thread.sleep(20000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "Thread-A");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " invoke");
try {
Thread.sleep(20000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "Thread-B");
threadA.start();
threadB.start();
Thread.sleep(1000);
System.out.println(threadA.getName()+" "+threadA.getState());
System.out.println(threadB.getName()+" "+threadB.getState());
}
结果:
线程A和线程B使用同一个锁,线程A先拿到锁,然后睡眠,所以状态是TIMED_WAITING,线程B没有拿到锁所以是BLOCKED的状态。
4、WAITING
等待状态,处于等待状态的线程是由于执行了4个方法中的任意方法。
1. Object的wait方法,并且没有使用timeout参数;
2. Thread的join方法,没有使用timeout参数,Thread.join() ,会让他的父线程阻塞waiting,直到thread执行完毕。
https://www.jianshu.com/p/fc51be7e5bc0
3. LockSupport的park方法。
4.没有时间参数的Condition的await方法
Condition的await方法跟Obejct的wait方法原理是一样的,故也是WAITING状态
处于waiting状态的线程会等待另外一个线程处理特殊的行为。 再举个例子,如果一个线程调用了一个对象的wait方法,那么这个线程就会处于waiting状态,直到另外一个线程调用这个对象的notify或者notifyAll方法后才会解除这个状态。或者一个线程调用了Thread.join(),等待指定的线程去终止。
public static void main(String[] args) {
final Object lock = new Object();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("threadA 开始");
try {
lock.wait(); // 释放锁,加入monitor的waitng_set队列
System.out.println("threadA wait over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "WAITING-Thread-A");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("threadB 开始");
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.notifyAll();
}
}
}, "WAITING-Thread-B");
threadA.start();
threadB.start();
}
打印结果:
首先threadA 抢到锁,输出“threadA 开始”,然后执行wait方法,释放锁,此时WAITING状态,然后threadB抢到锁,开始执行同步代码,输出"threadB 开始",然后睡眠20s,执行
notifyAll()方法,唤醒所有线程 包括threadA ,然后threadA 开始代码输出"threadA wait over".
5、TIMED_WAITING
有等待时间的等待线程所处的状态。一个线程处于这一状态是因为用一个指定的正的等待时间(为参数)调用了以下方法中的其一:
- Thread.sleep
- 带时限(timeout)的 Object.wait
- 带时限(timeout)的 Thread.join
- LockSupport.parkNanos
- LockSupport.parkUntil
例子:
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run() {
try {
synchronized (this){
System.out.println(Thread.currentThread().getName()+"同步开始");
wait(3000);
System.out.println(Thread.currentThread().getName()+"同步结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getState());
Thread.sleep(2000);
System.out.println("t1 "+t1.getState());
}
打印结果:
main RUNNABLE
Thread-0同步开始
t1 TIMED_WAITING
Thread-0同步结束
6、TERMINATED
已执行完毕的线程处于此状态。
例子:
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run() {
try {
synchronized (this){
System.out.println(Thread.currentThread().getName()+"同步开始");
wait(3000);
System.out.println(Thread.currentThread().getName()+"同步结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getState());
Thread.sleep(4000);
System.out.println("t1 "+t1.getState());
}
打印结果:
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run() {
try {
synchronized (this){
System.out.println(Thread.currentThread().getName()+" 同步开始");
wait(3000);
System.out.println(Thread.currentThread().getName()+" 同步结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getState());
Thread.sleep(4000);
System.out.println(t1.getName()+" "+t1.getState());
}
打印结果:
二.同步与异步&并发与并行
1. 同步与异步
同步: 排队执行 ,效率低但是安全
异步: 同时执行 ,效率高但是数据不安全
2. 并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
在并发中,我们可以将同步定义为一种协调两个或更多任务以获得预期结果的机制。
同步的方式有两种:
- 控制同步:例如,当一个任务的开始依赖于另一个任务的结束时,第二个任务不能再第一个任务 完成之前开始。
- 数据访问同步:当两个或更多任务访问共享变量时,再任意时间里,只有一个任务可以访问该变量。