线程与进程
进程:每个进程都有独立的内存空间,一个进程可以看做是一个软件或应用程序
线程:是进程中的多个执行路径,线程可以看作是软件里的执行路径。每个线程都有自己的栈空间,共用一个堆内存。由一个线程调用的方法,那么这个方法也在这个线程内执行
线程的一些概念:
线程的调度:
分时调度
抢占式调度(java中使用的是抢占式调度)
线程的分类:
线程分为守护线程和用户线程
- 守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。(可以使用
t1.setDaemon(true)
将线程设置为守护线程) - 用户线程:当一个进程不包含任何用户线程时,进程结束
线程的状态
- 新建(new):先创建的线程对象
- 可运行(就绪)(runnable):线程创建后,被其他线程调用了该线程的start()方法。该状态的线程位于可运行线程池中,等待cpu (处理机)的使用权 。
- 运行)(running):可运行状态的线程获得了cpu (处理机)时间片,开始执行程序代码。
- 阻塞(blocked):线程因为某种原因主动放弃了cpu 使用权,暂时停止运行。直到线程完成了需要等待的操作,才会转为就绪状态,再次争夺cpu使用权。
-
- 死亡(dead):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
同步与异步:
同步:排队执行,效率低,但是安全
异步:同时执行,效率高,但是数据不安全
什么是数据的安全和不安全?举个例子:
public class Demo1{
public static void main(String[] args){
Runnable run = new Ticket();
//让3个线程一起卖票
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
public void run(){
while(count>0){
//卖票
System.out.println("准备卖票");
count--;
System.out.println("出票成功,余票:"+count);
}
}
}
}
这段代码多次运行测试后,会出现如下情况:
我们发现,余票出现了-1,但并不是每次都会出现-1。出现这种情况的原因是:当第一个count减到1的线程运行到count减1时,此时count还没有减1,但是其他两个线程已经到了while循环,判断count大于0,成功进入循环,继续向下执行就导致了出现count小于0 的情况。线程执行的顺序并不是固定的,所以要多次执行会出现这种情况,并不是每次都会出现。
线程不安全的解决方案:
隐式锁:
- 同步代码块:synchronized(锁对象){代码块}//锁对象就是一个Object类型的变量,相同变量是一个锁。看同一把锁才能排队。
- 同步方法:在方法前加上synchronized修饰,进行排队
public synchronized boolean fangFaMing(){}
显示锁:
- 显示锁Lock子类 ReentrantLock
Lock l = new ReentrantLock();
……
l.lock();
……
l.unlock();
并发与并行
并发:指两个或者多个事件在同一时间段内发生
并行:至两个或者多个事件在同一时刻发生(同时发生)
如何用java实现线程
实现方法:
- 实现Runnable接口
- 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。
- 可以避免单继承所带来的的局限性(在java中,一个子类只能继承一个类,但是可以实现多个接口)
- 任务与线程本身是分离的,提高了程序的健壮性
- 继承Thread类
能够通过使用匿名内部类的方式简便地生成线程
创建好线程类之后,在方法中调用start()方法就可以实现一个新的线程了
Thread类中的方法:
- 已过时的方法:
stop():本质上是不安全的,如果一个正在执行的线程,它还在占用着资源,如果直接停止,可能会导致其所占用的资源无法释放,正确的停止方法是通知线程该结束了,让他return run()方法可以通过变量做标记的方式通知。 - 常用方法:
- 构造方法:
Thread()
Thread(Runnable target)指定线程所要做的任务
Thread(Runnable target,String name)指定线程所要做的任务,并且给他起一个名字
Thread(String name)传给线程所要做的任务名 - String getName() 返回线程的名称
- long getId() 返回线程的标识符
- int getPriority() 返回线程的优先级
- void setPriority(int newPriority)设置线程的优先级
- void start() 让线程开始执行,java虚拟机调用此线程的run方法
- void sleep(long millis)参数:毫秒。为当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性 进程执行时,遇到sleep就会休眠,休眠具体时长就是传入的参数,休眠结束才回继续向下执行,可以用于让程序每隔几秒输出一个东西
- void sleep(long millis,int nanos)参数:毫秒,纳秒。指定毫秒数加上纳秒数
- void setDaemon(boolean on) 将此线程标记为daemon线程(守护线程)或者用户线程main方法线程为主线程,从其他方法开启的线程叫子线程
设置和获取线程的名称
可以使用Thread.currentThread()获取当前正在运行的线程
创建线程的默认名称是Thread-0,Thread-1,Thread-2……
在创建时传入名称就可以设置线程的名称:
New Thread(new myRunnable(),”名称”);