多线程基础
线程的状态
- 创建状态
在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread 类的构造方法来实现,例如 “Thread thread=new Thread()”。
- 就绪状态
新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。
- 运行状态
当就绪状态被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的 run() 方法。run() 方法定义该线程的操作和功能。
- 阻塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
- 死亡状态
线程调用 stop() 方法时或 run() 方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行的能力
线程的优先级
在 Java 的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能会先被执行。(不是一定被先执行!)
class MyThread implements Runnable{ // 实现Runnable接口
public void run(){ // 覆写run()方法
for(int i=0;i<5;i++){
try{
Thread.sleep(500) ; // 线程休眠
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()
+ "运行,i = " + i) ; // 取得当前线程的名字
}
}
};
public class ThreadPriorityDemo{
public static void main(String args[]){
Thread t1 = new Thread(new MyThread(),"线程A") ;
Thread t2 = new Thread(new MyThread(),"线程B") ;
Thread t3 = new Thread(new MyThread(),"线程C") ;
t1.setPriority(Thread.MIN_PRIORITY) ; // 优先级最低
t2.setPriority(Thread.MAX_PRIORITY) ; // 优先级最高
t3.setPriority(Thread.NORM_PRIORITY) ; // 优先级最中等
t1.start() ;
t2.start() ;
t3.start() ;
}
};
同步及死锁
同步代码块
public class Demo02 {
public static void main(String[] args) {
//线程不安全
//解决方案1、同步代码块
//任何对象都可以添加锁的标记
//格式: sychronized(锁对象){
//
// }
Object o = new Object();
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
}else {
break;
}
}
}
}
}
}
同步方法
public class Demo03 {
public static void main(String[] args) {
//线程不安全
//解决方案2、同步方法
//任何对象都可以添加锁的标记
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
return true;
}
return false;
}
}
}
显示锁
public class Demo04 {
public static void main(String[] args) {
//线程不安全
//同步代码块 和 同步方法都属于隐式锁
//解决方案3、显示锁 Lock 子类 ReentrantLock
//任何对象都可以添加锁的标记
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count = 10;
//显示锁 lock : fair参数为true就是公平锁
private Lock lock = new ReentrantLock(true);
@Override
public void run() {
while (true) {
lock.lock();
try {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
} else {
break;
}
} finally {
lock.unlock();
}
lock.unlock();
}
}
}
}
线程死锁
所谓死锁,就是两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。
避免方法:
在任何一个产生锁的方法里,不要在调用其他可能产生锁的方法
public class Demo05 {
public static void main(String[] args) {
//线程死锁
//在任何一个产生锁的方法里,不要在调用其他可能产生锁的方法
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
//罪犯
static class Culprit{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放了人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯被放走了,罪犯也放了人质");
}
}
//警察
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人质,我放过你");
c.fun();
}
public synchronized void fun(){
System.out.println("人质获救了,罪犯被放走了");
}
}
}
线程池 Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性
Java中的四种线程池 . ExecutorService
1.缓存线程池
/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
public class Demo08 {
public static void main(String[] args) {
//创建缓存线程池
ExecutorService service = Executors.newCachedThreadPool();
//指挥线程池执行新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 定长线程池
/**
* 定长线程池.
* (长度是指定的数值)
* 执行流程:
3. 单线程线程池
4. 周期性任务定长线程池
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public class Demo09 {
public static void main(String[] args) {
//定长线程池
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
3. 单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
/**
* 单线程线程池.
* 执行流程:
* 1. 判断线程池 的那个线程 是否空闲
* 2. 空闲则使用
* 4. 不空闲,则等待 池中的单个线程空闲后 使用
*/
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
4. 周期性任务定长线程池
public class Demo11 {
public static void main(String[] args) {
//周期定长线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 1. 定时执行一次
* 参数1. 定时执行任务
* 参数2. 时长数字
* 参数3. 时长数字的时间单位,TimeUnit常量指定
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
},5, TimeUnit.SECONDS); */
/**
* 周期性执行任务
* 参数1. 任务
* 参数2. 延迟时长数字(第一次执行在什么时间以后)
* 参数3. 周期时长数字(每隔多久执行一次)
* 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("汗滴禾下土");
}
},5,1,TimeUnit.SECONDS);
}
}