两个或两个以上的线程在同时操作一个共享资源时仍然能得到正确的结果,则称为线程安全。
判断一个程序是否会有线程安全问题的标准
A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据
同步的特点:
前提: //多个线程 解决问题的时候要注意: //多个线程使用的是同一个锁对象 同步的好处 //同步的出现解决了多线程的安全问题。 同步的弊端 //当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步代码块:
synchronized(锁){ // 操作共享资源的代码 } 同步代码块的原理 能够保证同一时间只有一个线程执行代码块中的代码。 A:锁是什么呢? 任意类型的对象都可以充当锁。 B:需要同步的代码是哪些呢? 把多条语句操作共享数据的代码的部分给包起来 //注意: 同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。 多个线程必须是同一把锁。
public class TicketThread extends Thread { // 定义变量记录总票数 private static int tickets = 100; // 创建锁对象 private static Object lockObj = new Object(); public void run() { while (true) { try { // 模拟休眠 Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } // 使用循环保证票能够被卖完 synchronized (lockObj) { // 使用同步代码块实现线程安全 // 开始卖票 if (tickets > 0) { // tickets = 1 // 卖一张 System.out.println(this.getName() + " 卖了一张票,还剩 " + (--tickets) + " 张"); continue; } } System.out.println(this.getName() + " 票没了..."); break; } } }
同步方法实现线程安全
同步方法的格式: 修饰符 synchronized 返回值类型 方法名(参数列表){ } 同步方法的原理 * 能够保证同一时间只有一个线程执行方法体中的代码。 同步方法的注意事项 * 非静态同步方法:锁对象默认是:this * 静态同步方法:锁对象默认是:类名.class * 每一个类都会有一个Class对象,而且是唯一的。
示例代码
public class TicketThread extends Thread { // 定义变量记录总票数 private static int tickets = 100; public void run() { while(tickets > 0) { try { // 模拟休眠 Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } // 调用方法卖一张票 saleTicket(); } System.out.println(this.getName() + " 票没了..."); } /** * 用来卖一张票 */ public synchronized static void saleTicket() { if(tickets > 0 ) { // 卖一张 System.out.println(Thread.currentThread().getName() + " 卖了一张票,还剩 "+(--tickets)+" 张"); } } }
start和run方法区别
start方法会创建线程,并在新的路径中执行run方法的代码
start方法内部会触发run方法执行。
run方法不会创建线程,会在当前线程中执行run方法的代码。
小结
进程的概念
正在运行中的程序就是进程
线程的概念
进程中的一个独立的执行路径。
线程的分类
单线程:同一时间只能干一件事
多线程:同一时间能干多件事情。
主线程:程序启动系统自动创建并执行main方法的线程。主线程的执行入口:main方法
子线程:除了主线程以为的其他线程,子线程的执行入口:run方法
线程运行模式
分时式模式
抢占式模式:Java属于该种模式
线程的常用方法
void setName(String name) String getName(); static Thread currentThread(); static void sleep(long mills)
线程的五种状态
新建:刚刚创建出来,还没start
就绪:调用start方法或睡醒或唤醒
运行:抢到CPU使用权
阻塞:调用sleep或wait方法
死亡:调用stop方法或任务正常执行完毕。
线程安全概述
两个或多个线程同时操作一个共享资源时仍然能得到正确的结果。
实现线程安全的技术
同步代码块
synchronized(锁){ // 操作共享资源的代码 } 锁对象可以是任意类型的对象但必须被所有线程共享。
同步方法
修饰符 synchronized 返回值类 方法名(参数列表){ } // 默认锁对象是:this 修饰符 static synchronized 返回值类 方法名(参数列表){ } // 默认锁对象是:类名.class