引入
进程:简单的理解进程就是一个应用程序
线程: 就是进程的最小执行单位,是一个独立的功能
多线程作用:
1、为了利用计算机的多核心优势
2、提高程序运行效率
创建方式
一、继承Thread类
步骤:
1、写一个线程类XxxThread extends Thread类
2、重写run()方法
3、写一个测试类,在测试类中创建线程对象—>调用对象的start()启动线程
二、实现Runnable接口
步骤:
1、写一个实现业务类XxxRunnableImpl implements Runnable接口
2、重写run()方法
3、写一个测试类,在测试类中先创建业务类对象—>再通过构造方法创建线程对象(业务类对象)—>调用线程对象的start()启动线程
注意事项:
1、只有继承Thread类的子类才能叫线程类,实现Runnable接口的子类叫业务类
2、启动线程类是两步:创建对象—>对象.start(),
启动业务类是三步:创建业务类对象—>创建线程对象(业务类对象)—>线程对象.start()
3、start()会去执行线程里的run()方法,但只有start()才是启动线程,run()只是在执行方法
====================================================================
同步方式
一、悲观锁(以最坏的情况做打算)
1、同步代码块
synchronized(同步对象){
//有线程安全问题的代码;
}
//注意事项:作为同步对象需要满足的条件:只有一份(静态区中的静态成员或元空间中的字节码文件)
2、同步方法
用synchronized关键字修饰方法即可,写在返回值前面,方法体里是有线程安全问题的代码
注意事项:因为创建业务类对象只需要一个,然后通过创建线程类对象来启动
1、若是继承类使用同步代码块,通过同步对象控制共享,所以必须是只有一份的;若是业务类,this即可
2、若是继承类使用同步方法,方法必须要static修饰;若是业务类,就不要求
二、乐观锁
锁机制:Lock接口的实现类ReentrantLock
构造方法:
- ReentrantLock() 创建一个 ReentrantLock的实例,获取锁的几率不同
- ReentrantLock(boolean fair) 根据给定的公平政策创建一个 ReentrantLock的实例。 理论上获取锁的几率是相同的
class X {
//1.创建一个 ReentrantLock的实例。不要公平机制,效率高
private final ReentrantLock lock = new ReentrantLock();
/**
* 在run()中进行调用
*/
public void m() {
lock.lock(); // 上锁
try {
//有线程安全问题的代码
} finally {
lock.unlock();//释放锁,不然后面其他线程无法执行代码
}
}
}
注意事项:
1、悲观锁和乐观锁是一个总称,不仅仅包括这三种方法
2、锁原则:最小范围原则(锁住的代码块最小化),为了保证执行效率,锁机制效率高且功能更加强大,建议使用
3、同步方法最简单,如果能够满足性能要求,建议使用同步方法
==============================================================
经典案例:多窗口售票(继承类+代码块)
案例描述:总票数:50,三个窗口同时售卖
继承类:
public class SaleTicketTread extends Thread {
private static int ticket=50;
public SaleTicketTread() {
super();
}
/**
* 给线程起名,为了打印看输出方便
* @param name
*/
public SaleTicketTread(String name) {
super(name);
}
@Override
public void run() {
while (ticket>0) {
sale();
}
}
private void sale() {
synchronized (SaleTicketTread.class) {//只有一份的对象
if (ticket>0) {//虽然锁住了,但是还是会出现多个线程同时进入run,ticket未--,所以在下一个线程执行售票之前,先判断ticket是否卖光
System.out.println(Thread.currentThread().getName()+",票号:"+ticket);
ticket--;
}
}
}
}
测试类
public class TicketTest {
public static void main(String[] args) {
SaleTicketTread ticket1= new SaleTicketTread("窗口1");
SaleTicketTread ticket2= new SaleTicketTread("窗口2=====");
SaleTicketTread ticket3= new SaleTicketTread("窗口3=========");
ticket1.start();
ticket2.start();
ticket3.start();
}
}
问题代码分析:为何需要if (ticket>0)