1.线程
一个线程就是一个正在运行的程序;就是程序执行的线路(路径);
如果程序中同时有两个或两个以上的程序在执行,这个程序叫做多线程程序;
进程:是应用程序的载体,当软件一运行起来的时候,系统就会给这个软件创建一个新的进程。一个应用程序(软件)对应一个进程;
一个进程里面可以包含多个线程,一个线程里面只能包含一个进程;
好处:
提高软件的运行效率(性能);
1.1 多线程程序运行的原理
在同一时刻只能有一个线程正在运行CPU。
CPU资源的调度方式:
第一种:分时调度
操作系统会给多个线程平均分配CPU的时间;
第二种:抢占式调度
根据线程的优先级分配CPU资源,优先级高的线程最先得到CPU资源;
2.开发多线程程序
运行main方法的线程叫做主线程;
开发多线程程序有两种方式:
1.继承Thread类
构造方法:
Thread():创建线程对象,线程名字有默认的;
Thread(String name):使用执行的名字创建线程对象;
成员方法:
String getName():得到线程的名字;
void run():运行现成的逻辑代码的;
void start():开启线程,只是开启线程处于就绪状态,并不是运行线程,当操作系统给它分配CPU资源后才能处于运行状态调用run方法;
格式:
修饰符 class 类名 extends Thread{
重写run方法(){
子线程执行的代码;
}
}
使用子线程步骤:
1.定义子线程类,继承父类Thread类,重写run方法;
2.创建子线程对象;
3.调用对象的stert()方法;
得到线程的名字:
Thread.currentThread():得到当前线程对象;
String getName():得到线程的名字;
一行代码搞定:
Thread.currentThread().getName():得到线程的名字;
2.实现Runnable接口
步骤:
1.定义一个类,实现Runnable接口,重写run方法,这个中写子线程要执行的代码;
2.创建子任务对象;
3.创建子线程对象:Thread t=new Thread(子任务对象);
4.调用子线程对象的start方法;
两种方式的区别:
1.第一种方式把任务的代码与子线程的代码耦合带一起了,第二种方式 把任务的代码和子线程的代码分开了,解耦性强;
2.第一种方式继承了Thread类,具有继承的局限性,扩展性不强,第二种实现了接口,还可以继承其他父类,扩展性强;
3.非线程安全的问题
三子线程共卖100张票,出现了卖重复的票,第0张票,副票;
原因:
多个线程在改变一个共享的数据时, 出现了某一个线程不想要的结果.
解决办法:
保证修改共享数据的代码在同一时刻只能被一个线程执行,保证线程是同步的;
常用的方式有两种:
1.使用同步代码块
使用关键字syncronized修饰一个代码块:修改共享数据的代码;
格式:
syncronized(锁对象){
修改共享数据的代码;
}
同步:代码从上往下一行一行的执行;
异步:多个线程同时执行;
锁对象:
用来锁住同步代码块的,只有获得这个锁对象的线程才能执行同步代码块,线程执行完同步代码块后,释放锁对象,然后其他线程才能得到锁对象;
示例代码:
public class TicketRunnable implements Runnable {
// 共享的100张票,可以让三个窗口(子线程)卖这100张票
int ticketNumber = 100;
// 作为锁对象
Object obj = new Object();
@Override
public void run() {
while (true) {
// 只有获得obj对象的线程才能执行下面的代码.
// synchronized(obj){
// 可以使用this作为锁对象,因为三个线程使用的是同一个TicketRunnable对象,this就是TicketRunnable对象
synchronized (this) {
if (ticketNumber > 0) {
// 让当前线程睡眠50毫秒.模拟网络延迟的时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + (ticketNumber--) + "张票");
}
}
}
}
}
2.使用同步方法
格式:
修饰符 syncronized 返回值类型 方法名(参数列表){
修改共享数据的代码;
}
示例代码:
public class TicketRunnable implements Runnable {
// 共享的100张票,可以让三个窗口(子线程)卖这100张票
int ticketNumber = 100;
// 作为锁对象
Object obj = new Object();
@Override
public void run() {
while(true){
//调用同步方法
sell();
}
}
// 同步方法使用的锁对象默认是this
public synchronized void sell(){
if(ticketNumber > 0){
// 让当前线程睡眠50毫秒.模拟网络延迟的时间
try {
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ (ticketNumber--) +"张票");
}
}
}
3.lock锁接口
Lock lock = new ReentrantLock();
// 获得锁对象
lock.lock();
同步代码块;
//释放锁对象
lock.unlock();
同一时刻只能有一个
4.线程状态
新建状态:创建了线程对象后;
就绪状态:调用start方法后;
运行状态:调用run方法后;
等待状态:锁对象正被其他线程使用,自己没有获得锁对象.
休眠状态:自己获得了锁对象,但是调用了自己的sleep方法,自己处于休眠状态.
结束(死亡)状态:run方法执行完毕后;