进程的概念
进程就是正在执行的程序
线程的概念
线程是程序中的一个执行流
进程和线程的关系
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个主线程。
多线程的概念
多线程是指程序中包含多个执行流,在一个程序中可以同时运行多个不同的线程来执行不同的任务
多线程的作用
可以同时并发执行多个任务
最大限度地减低CPU的闲置时间,从而提高CPU的利用率
主线程的概念
任何一个Java程序启动时,一个线程立刻运行,它执行main方法,这个线程称为程序的主线程
主线程的作用
它是产生其它线程子线程的线程
主线程必须最后结束,因为它要执行其它子线程的关闭工作。
线程的生命周期
新线程、准备就绪、运行、等待/阻塞、结束

线程创建的两种方式
通过继承Thread类
定义:
public class MyThread extends Thread{
public void run()
{......}
}
调用:
MyThread thread = new MyThread();
thread.start();
通过实现Runnable接口
定义:
public class MyThread implements Runnable{
public void run()
{......}
}
调用:
MyThread r = new MyThread();
Thread thread = new Thread(r); //创建一个线程作为外壳,将r包起来,
thread.start();
也可以使用内部类的方式通过Runnable实现
定义:
Runnable runnable = new Runnable()
{
public void run()
{......}
};
调用:
Thread thread = new Thread(runnable);
thread.start();
匿名内部类
匿名内部类也就是没有名字的内部类
因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
使用匿名内部类的前提条件是必须继承一个父类或实现一个接口
匿名内部类不能有构造方法
匿名内部类不能定义任何静态成员、方法和类
匿名内部类不能是public、protected、private、static
只能创建匿名内部类的一个实例
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类
因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效
在匿名类中用this时,这个this指的是匿名类本身。如果我们要使用外部类的方法和变量的话,应该加上外部类的类名

线程的启动和停止
启动
针对同一个线程对象,不能多次执行start()方法,否则会引发程序异常近况发生
start()方法的调用后并不是立即执行多线程代码,而是使该线程变为就绪状态,什么时候运行是由操作系统调度决定的

停止
线程对象的run()方法所有代码执行完成后,线程会自然消亡
stop()方法用于停止一个已经启动的线程,但是它已经被废弃,不建议使用,因为它本质上就是不安全的
如果需要在运行过程提前停止线程,可以通过设置共享变量值和调用interrupt()方法引发异常的方式结束线程

处理一个线程由于等待某些事件的发生而被阻塞时需要停止该线程的问题
用Thread提供的interrupt()方法,
该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,
从而使线程提前结束阻塞状态,退出堵塞代码

注意interrupt()方法并不能阻断I/O阻塞或线程同步引起的线程阻塞
如何处理由I/O资源引起的线程阻塞时的线程中断问题
通过关闭底层I/O通道,人为引发异常从而进行共享变量重新赋值而跳出线程的run()方法,

在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。
一个是静态的方法interrupted() 用来判断当前线是否被中断
一个是非静态的方法isInterrupted() 用来判断其他线程是否被中断

线程不同状态之间的转换
Thread Created ---->
--new--> 创建 --start()--> 准备 --stop()--> 结束
--new--> 创建 --start()--> 准备 --run()--> 运行 --yield()、超时--> 准备
--new--> 创建 --start()--> 准备 --run()--> 运行 --stop()、完成--> 结束
--new--> 创建 --start()--> 准备 --run()--> 运行 --suspend()、sleep()、wait()--> 阻塞 --stop--> 结束
--new--> 创建 --start()--> 准备 --run()--> 运行 --suspend()、sleep()、wait()--> 阻塞 --resume()、notify()、notifyAll()--> 准备

用户线程 (User Thread)
守护线程 (Daemon Thread)
所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程、比如垃圾回收线程、异常处理线程
只要任何非守护线程还在运行,程序就不会终止

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现
默认守护线程的属性为false,即默认创建的线程对象为非守护的用户线程
在使用守护线程时需要注意一下几点
thread.setDaemon(true)必须在thread.start()之前设置
否则会抛出一个IllegalThreadStateException异常,不能把正在运行的常规线程设置为守护线程
在Daemon线程中产生的新线程也是Daemon的
可以通过线程对象的isDaemon()方法判定该线程是否为守护线程

线程类重要方法的作用
getName()方法可以获取特定线程的名字
setName(String name)方法可以给定线程名字,如果没有为线程显式提供名字,Java将按照thread-0,thread-1…thread-n的方式为线程提供默认的名字
sleep()方法使当前线程进入被阻塞的状态,即使没有其他等待运行的线程,当前线程也会等待指定的时间,可以使优先级低的线程得到执行的机会
yield()方法将当前线程转入暂停执行的就绪状态,如果没有其他等待执行的线程,当前线程会马上恢复执行,只能使相同或更高优先级的线程有执行的机会
join()方法用于等待其它线程结束,当前运行的线程可以调用另一线程的join方法,当前运行线程将转到阻塞状态,直至另一线程执行结束,它才会恢复运行

线程的优先级
Java线程可以有优先级的设定,取值范围是1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
除了Thread.MIN_PRIORITY和Thread.MAX_PRIORITY外,Thread还提供了另一个常量Thread.NORM_PRIORITY(5)
Java线程在没有明确指定的情况下,其优先级并不一定是NORM_PRIORITY,而是和父线程(创建本线程的线程)的优先级保持一致,main线程的优先级是NORM_PRIORITY
可以通过Thread类的setPriority()方法(该方法被final修饰,不能被子类重载)更改优先级。

线程同步
主要的作用是实现线程安全的类

不同步会发生问题
解决:通过设置令牌的方法,线程A执行的时候获取令牌,线程B要执行只能等线程A执行完了拿到令牌才能执行

关键字:volatile,它用于在声明时修饰变量
作用是:保证变量在内存模型中的可见性、禁止指令重排序:对volatile变量的操作不能进行重排序

关键字:synchronized,作用是获得对象锁,实现线程同步
实现同步有两种方法:同步方法、同步代码

线程死锁
某个线程还在执行,锁没有释放的时候就有别的对象来访问,这时只能等待,而后又来了新的线程,只能继续等待,线程等待连续循环被称为死锁
造成死锁必须同时满足四个条件:
互斥条件:线程使用的资源中至少有一个是不能共享
至少有一个进程持有一个资源,并且它在等待获取一个当前被别的进程持有的资源.
资源不能别进程抢占
必须有循环等待

如何防止死锁
只需破坏四个条件其中一个即可
防止死锁最容易的方法是破坏条件4

线程通讯
线程通信的目标是使线程间能够互相发送信号
另一方面,线程通信使线程也能够等待其他线程的信号

Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态
提供wait()、notify()和notifyAll()来实现该等待机制

sleep方法由Thread类提供,它不会释放线程锁,wait方法由Object类提供,这就意味着所有的Java类都具备wait方法,
wait方法调用后会释放线程具备的锁,同一个对象的notify和notifyAll方法能够唤醒线程,wait方法和notify方法需要在同步块中调用