并发:两个或多个事件在同一个时间段内发生 并行:两个或多个事件在同一个时刻发生(同时)
进程:指一个内存中运行的应用程序(进入到内存中) 线程:是进程中的一个执行单元 多线程好处:效率高 各个单元互不影响
分时调度:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间 抢占式调度:优先让优先级高的线程使用CPU,如果优先级相同,随机选择一个,Java使用的是抢占式调度
执行主(main)方法的线程 单线程程序:只有一个线程,从main方法开始,从上往下执行
JVM执行main方法,main方法进入栈内存 JVM会找操作系统开辟一条main方法路径通向CPU(叫做主线程) CPU通过这个路径执行main方法
在子类中重写Thread类中run方法,设置线程任务 创建子类对象 调用start方法,开启线程,执行run方法 void start() 使该线程开始执行 Java虚拟机调用run方法 结果 main线程与该run线程并发运行 且多次启动一个线程是非法的。特别是线程结束后不能重新启动
public class Thread1 extends Thread{ @Override void run(){ //覆写run的内容 } public static void main(String []args){ Thread1 mt = new Thread1(); mt.start(); // mt的run加线程入 //main 线程内容 } }Thread 中的 String getName(); 返回该线程的名称 static Thread currentThread(); 返回当前正在执行的线程对象的引用
public class MyThread extends Thread{ public MyThread(){} public MyThread(String name){ super(name); } public static void main(String []args){ MyThread mt = new MyThread(); mt.setName("new name"); mt.start(); MyThread("another name").start(); } @Override public void run(){ String name = getName(); System.out.println("name"); Thread t = Thread.currentThread(); name = t.getName(); System.out.println("name"); } }该接口只含一个run方法 创建一个Runnable接口的实现类 实现类必须定义一个称为run的无参数方法 创建一个实现类对象 创建Thread类对象,传递实现类对象 调用start() 启动线程
public class MyThread implements Runnable{ public static void main(String []args){ MyThread mt = new MyThread(); Thread t = new Thread(mt); t.start(); } @Override public void run(){ // run的重写方法 } }避免了单继承的局限性(一个类只能继承一个类) 增强了程序的扩展性 降低了程序的耦合性 实现Runnable接口的方式 把设置线程任务和管理线程分开
匿名内部类产物:子类/实现类对象
new 父类/接口(){ //重复父类/接口的方法 } public class MyThread extends Thread{ public static void main(String []args){ new Thread(){ public void run(){ //覆写内容 } }.start(); // Runnable r = new Runnable(){ public void run(){ //覆写内容 } } new Thread(r).start(); //或者 new Thread(new Runnable(){ public void run(){ //覆写内容 } }).start(); } }同步中的线程 没有执行完不会归还锁对象
synchronized(锁对象){ //访问共享数据的代码 //保证多个线程使用的锁对象是同一个 //把同步代码块锁住 只让一个线程在同步代码块中执行 } public class RunnabkeImpl implements Runnable{ private int ticket=100; Object obj = new Object(); @Override public void run(){ while(true){ synchronized(obj){ if(ticket>0){ System.out.println("ticket"); ticket--; } } } } }静态的同步方法 锁对象是本类的class属性–>class 文件对象(反射)
lock接口中的方法
void lock() void unlock()在成员位置创建一个ReentrantLock对象 在可能会出现安全问题的代码前调用lock方法获取锁 执行代码后 调用unlock 解锁
public class RunnabkeImpl implements Runnable{ private int ticket=100; Object obj = new Object(); Lock lock = new ReentrantLock(); @Override public void run(){ while(true){ lock.lock();// 加锁 if(ticket>0){ System.out.println("ticket"); ticket--; } lock.unlock();//解锁 } } }Timed Waiting 计时等待
限时暂停一个线程 如使用sleep() 语句 进入到一个计时等待的状态 注意: 进入TIMED_WAITING 状态 一般调用sleep方法。单线程也可以调用 为了让其他线程有机会执行,将Thread.sleep() 的调用放线程run() 之内 sleep() 与 锁 无关,线程睡眠到期自动苏醒 并返回刀Runnable状态 sleep() 指定的时间是线程不会运行的最短时间,睡眠结束后线程不一定马上开始block 锁阻塞状态
一个正在阻塞等待一个监视器锁(锁对象)的线程处于的状态 如线程A与线程B使用同一锁,如果线程A获取到锁,线程A进入Runnable状态 而线程B进入Blocked锁阻塞状态waiting 无限等待
一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态 这其实并不是一个线程操作,而是多个线程间的通信 多个线程会争取锁,同时相互之间又存在协作关系 像争夺唯一一台电脑时,第一个大佬把题AC了(调用wait() 进入Waiting状态) 放出电脑(失去同步锁) 此时notify() 另外两个大佬 告诉他俩可以抢电脑(同步锁)AC题(run),然后其中一个大佬就抢到了(进入Runnable) 另一个大佬想出题却没有抢到电脑(锁对象) 就只能自闭(Blocked)等到上面大佬写完(释放锁对象)wait方法可以带参数,差不多相当于sleep的作用(计时等待)在等待中可以notify退出休眠 不带参数就只能等notify(无限等待)
多个线程的竞争机制
wait() //线程不再活动,不再参与调度,进入wait set //不会浪费CPU资源 也不会竞争锁 //等到别的线程执行notify时才能从wait set中释放 notify() //选取所通知对象的wait set中的一个线程释放 notifyall() //释放所通知对象的wait set 上的全部线程食客类
public class ChiHuo extends Thread { private BaoZi bz; public ChiHuo(String name, BaoZi bz) { super(name); this.bz = bz; } @Override public void run() { while (true) { synchronized (bz) { if (bz.flag == false) {//没包子 try { bz.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("吃货正在吃" + bz.pier + bz.xianer + "包子"); bz.flag = false; bz.notify(); } } } }包子铺
public class BaoZiPu extends Thread { private BaoZi bz; public BaoZiPu(String name, BaoZi bz) { super(name); this.bz = bz; } @Override public void run() { int count = 0; //造包子 while (true) { //同步 synchronized (bz) { if (bz.flag == true) {//包子资源 存在 try { bz.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 没有包子 造包子 System.out.println("包子铺开始做包子"); if (count % 2 == 0) { bz.pier = "冰皮"; bz.xianer = "五仁"; } else { bz.pier = "薄皮"; bz.xianer = "牛肉大葱"; } count++; bz.flag = true; System.out.println("包子造好了:" + bz.pier + bz.xianer); System.out.println("吃货来吃吧"); //唤醒等待线程 (吃货) bz.notify(); } } } }测试类
public class Demo { public static void main(String[] args) { //等待唤醒案例 BaoZi bz = new BaoZi(); ChiHuo ch = new ChiHuo("吃货", bz); BaoZiPu bzp = new BaoZiPu("包子铺", bz); ch.start(); bzp.start(); } }