什么是程序?什么是进程?什么是线程?
程序:一组静态代码
进程:正在进行着的一组程序
线程:进程中的一个单元
主要学习多线程使用
一个线程运行状态
- 创建状态: new 一个线程对象
- 就绪状态:调用对象的start方法
- 运行状态: 一个线程的运行是由系统(cpu)分配的,不是自己可控制的
- 阻塞状态:当一个线程,进行某些耗时操作时,会让出cpu资源,进入阻塞状态,重新进入就绪状态,等待cpu分配资源
- 死亡状态:当线程遇到异常或程序执行完毕,会进入死亡状态
如何使用线程
方式一(继承Thread) :
- 创建线程类,继承Thread
- 重写run方法
- 创建线程对象,调用start方法
public class RunnerMan extends Thread {
private String name;
public RunnerMan(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println(this.name+"跑了 "+i+" 米");
}
}
}
// =----------------------------------------
public class Main {
public static void main(String[] args) {
RunnerMan r1 = new RunnerMan("苏炳添");
RunnerMan r2 = new RunnerMan("博尔特");
RunnerMan r3 = new RunnerMan("加特林");
r1.start();
r2.start();
r3.start();
}
}
方式二(实现Runnable接口,为了解决不能多继承的问题):
-
创建线程类,实现Runnable
-
重写run方法
-
创建线程对象,创建Thread对象 ,然后把此对象作为Thread对象参数
-
调用Thread对象的start方法
public class RunnerMan implements Runnable { private String name; public RunnerMan(String name){ this.name = name; } @Override public void run() { for (int i = 0; i < 101; i++) { System.out.println(this.name+"跑了 "+i+" 米"); } } } // ---------------------------------- public class Main { public static void main(String[] args) { RunnerMan r1 = new RunnerMan("苏炳添"); RunnerMan r2 = new RunnerMan("博尔特"); RunnerMan r3 = new RunnerMan("加特林"); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); Thread t3 = new Thread(r3); t1.start(); t2.start(); t3.start(); } }
-
售票小练习
public class Main { public static void main(String[] args) { // 做小练习:抢票系统 // 分析: Ticket类 System12306类 Window类 Window w1 = new Window("北京南站"); Window w2 = new Window("北京西站"); Window w3 = new Window("北京东站"); w1.start(); w2.start(); w3.start(); } } // ----------------------------- /* * 这是一javaBean对象,专门用来存储数据,又叫做pojo(简单的java对象) * */ public class Ticket { private String startStation; private String stopStation; private Double price; public Ticket() { } public Ticket(String startStation, String stopStation, Double price) { this.startStation = startStation; this.stopStation = stopStation; this.price = price; } public String getStartStation() { return startStation; } public String getStopStation() { return stopStation; } public Double getPrice() { return price; } public void setStartStation(String startStation) { this.startStation = startStation; } public void setStopStation(String stopStation) { this.stopStation = stopStation; } public void setPrice(Double price) { this.price = price; } } // ----------------------------- import java.util.Vector; public class System12306 { // 由于系统是由几个窗口通用的,所以需要使用单例模式 private System12306(){}; private static System12306 system = new System12306(); public static System12306 getInstance(){ return system; } private Vector<Ticket> tickets = new Vector<>(); // 由于Vector 线程安全的,这就保证了在同一时间,只存在一个操作 // 创建100 张 票 // 静态代码块,在创建对象之前,通常用于初始化 { for (int i = 1; i < 101; i++) { tickets.add(new Ticket("北京"+i,"海南"+i,(i+100)/5*0.6)); } } // 创建取票方法 public Ticket getTicket(){ try{ return tickets.remove(0); }catch(Exception e){ return null; } } } // ----------------------------- public class Window extends Thread{ // 窗口使用,多线程并发,进行售票,公用一个系统 private String stationName; public Window(String stationName){ this.stationName = stationName; } // 重写run方法 @Override public void run() { sellTicket(); } private void sellTicket() { System12306 system = System12306.getInstance(); while (true){ Ticket ticket = system.getTicket(); if(ticket == null){ System.out.println("没有票了"); return; } System.out.println(this.stationName+" ["+ticket.getStartStation()+" ---- "+ticket.getStopStation()+" :"+ticket.getPrice()+"]"); } } } // -----------------------------
存在的问题
问题一:现在所使用的Vector,是线程同步的,如何自定义线程非同步的容器
问题二: 如何进行各线程状态之间的切换
用一个例子:生产者消费者模型
public class Main {
public static void main(String[] args) {
// 生产者消费者模型
Producer p1 = new Producer();
Consumer c1 = new Consumer();
Consumer c2 = new Consumer();
p1.start();
c1.start();
c2.start();
}
}
// -----------------------------
import java.util.ArrayList;
public class Warehouse {
// 单例模式
private Warehouse(){};
private static Warehouse warehouse = new Warehouse();
public static Warehouse getInstance(){
return warehouse;
}
private ArrayList<String> wares = new ArrayList<String>();
// 用于生产者调用
public void add() {
if(wares.size()<10){
wares.add("a");
}else{
return;
}
}
// 用于消费者调用
public void get(){
if(wares.size()>0){
String remove = wares.remove(0);
}else{
return;
}
}
}
// -----------------------------
public class Producer extends Thread {
public Warehouse warehouse = Warehouse.getInstance();
@Override
public void run() {
while(true){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.warehouse.add();
System.out.println("生产一个包子");
}
}
}
// -----------------------------
public class Consumer extends Thread{
public Warehouse warehouse = Warehouse.getInstance();
@Override
public void run() {
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.warehouse.get();
System.out.println("消费一个包子");
}
}
}
// -----------------------------
// 这样会报错,ArrayIndexOutOfBoundsException
// why?
// 通过这个模型成功的演示了线程安全问题
// 两个消费者,同时访问同一个仓库对象,并发访问,产生抢占资源问题,导致ArrayIndexOutOfBoundsException
// 解决方案: 加锁
如何进行加锁?
特征修饰符:synchronized
线程安全锁 两种方式
-
方式1:将synchronized关键字,放在方法的结构上
public synchronized void get(){}
锁定的是调用方法时的那个对象,在这里是消费者
-
方式2:将synchronzied关键字 放在方法(构造方法 普通方法 块)内部
public void get(){
好多代码
synchronzied(对象){
操作异步的代码;
}
好多代码
}
各种状态之间的切换
// -----------------------------
import java.util.ArrayList;
public class Warehouse {
// 单例模式
private Warehouse(){};
private static Warehouse warehouse = new Warehouse();
public static Warehouse getInstance(){
return warehouse;
}
private ArrayList<String> wares = new ArrayList<String>();
// 用于生产者调用
public synchronized void add() {
if(wares.size()<10){
wares.add("a");
}else{
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 用于消费者调用
public synchronized void get(){
if(wares.size()>0){
String remove = wares.remove(0);
}else{
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 其他类中的代码不变
如果没有线程锁,导致的问题
- ArrayIndexOutOfBoundsException :数组越界异常
- IllegalMonitorStateException: 不合法监视异常
笔试题
-
程序 进程 线程 之间的区别
-
线程的创建方式
-
线程的几种状态之间的切换
-
sleep方法 和 wait方法之间的区别
区别点 sleep wait 类 Thread Object 调用 静态方法 任何对象 理解 哪个位置调用,哪个线程等待 访问对象的其他线程等待 唤醒 不需要 其他对象唤醒(notify| notifyAll) 锁 不会释放锁 等待后会释放锁