目录
一、相关概念
- 进程:正在运行的应用程序(官方概念:一个正在运行的程序拥有该程序运行的所有资源,包括资源的分配和任务调度。)
- 线程:进程中的执行单元(作用:提升应用程序的执行效率)(官方概念:在进程中负责具体代码的执行,一个进程至少有一个线程。)
- 并行:两个应用程序跑在两个不同的核上同时进行
- 并发:交叉交替执行的任务
二、实现线程的两个方法
方法一:
创建一个类,继承Thread类,重写run方法,再调用start方法(单继承,有限制,有开辟线程的能力,资源共享方面不是很方便)
代码演示一:
MyThread类
public class MyThread extends Thread{ @Override public void run(){ run1(); } public void run1(){ for(int i = 0; i < 100; i++){ System.out.println("MyThread---->" + i); } } }
ThreadDemo类
package ThreadDemo; public class ThreadDemo { public static void main(String [] args){ MyThread myThread = new MyThread(); myThread.start(); for(int i = 0; i< 100; i++){ System.out.println("main---->" + i); } } }
运行结果:
截取一小部分看个大概,知道意思就行^_^
代码演示二:
TicketsThread类
public class TicketsThread extends Thread{ private int num; public TicketsThread(){} public TicketsThread(int num){ this.num = num; } public void setNum(int num){ this.num = num; } public int getNum(){ return this.num; } @Override public void run(){ while(this.num > 0){ sale(); } } public void sale(){ this.num = --num; System.out.println(Thread.currentThread().getName() + "Thread--->剩余票数:" + this.num); this.setNum(num); } }
ThreadDemo类
public class ThreadDemo { public static void main(String [] args){ TicketsThread ticketsThread = new TicketsThread(1000); ticketsThread.start(); while(ticketsThread.getNum() > 0){ ticketsThread.sale(); } } }
运行结果:
卖票的过程为后面做铺垫。
方法二:
实现Runnable接口,重新run方法。在传给Thread构造器,调用时调用Thread的start方法。(多实现,更灵活,推荐使用,没有开辟线程的能力,要将创建的对象交给指定线程来运行)。
代码演示:
RunnableTest类
public class RunnableTest implements Runnable{ @Override public void run(){ for(int i = 0; i < 100; i++){ System.out.println("分支线程" + i); } } }
Test类
public class Test { public static void main(String [] args){ RunnableTest1 r1 = new RunnableTest1(); Thread t1 = new Thread(r1); t1.start(); RunnableTest1 r2 = new RunnableTest1(){ @Override public void run(){ for(int i = 0; i< 100;i++){ System.out.println("main线程" + i); } } }; // 内部类 Thread t2 = new Thread(r2); t2.start(); } }
运行结果:
三、多线程实现售票
Tickets类
public class Tickets extends Thread{ private int num; public Tickets(){} public Tickets(int num){ this.num = num; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }
Operate类
public class Operate { private Tickets tickets; public Operate(Tickets tickets){ this.tickets = tickets; } public void sale(){ synchronized (this){ // synchronized 锁 同步 只能锁对象 try{ Thread.sleep(100); } catch (InterruptedException e){ e.printStackTrace(); } } if(tickets.getNum() > 0){ int num = tickets.getNum(); System.out.println(Thread.currentThread().getName() + " 正在售卖 " + tickets.getNum() + " 张票,剩余 " + (tickets.getNum() - 1)); num--; tickets.setNum(num); } } }
Test类
public class Test { public static void main(String [] args){ Tickets tickets = new Tickets(100); Operate operate = new Operate(tickets); new Thread(() -> { while(tickets.getNum() > 0) operate.sale(); },"窗口一").start(); new Thread(() -> { while(tickets.getNum() > 0) operate.sale(); },"窗口二").start(); new Thread(() -> { while(tickets.getNum() > 0) operate.sale(); },"窗口三").start(); } }
运行结果:
可以看到运行结果没有跟上面代码运行结果一样有重复值了
四、补充说明
1.sleep方法:
是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但监控状态依然保持,到时间后会自动恢复为就绪状态。(sleep方法上面代码有演示,在这就不单独拿出来了)
2.join方法:
作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。方法join具有使线程排队运行的作用,有些类似同步的运行效果。
3.join方法代码演示:
情况一:没有join方法
public class Test { public static void main(String [] args){ System.out.println("main线程开始!"); Thread t = new Thread(() -> { for(int i = 0; i < 100; i++){ System.out.println("分支线程在执行--->" + i); } }); t.start(); System.out.println("main线程结束!"); } }
运行结果:
从上面可以清晰的看到虽然分支线程写在了中间,但是main线程的开始和结束是黏在一起的
情况二:有join方法
public class Test { public static void main(String [] args){ System.out.println("main线程开始!"); Thread t = new Thread(() -> { for(int i = 0; i < 100; i++){ System.out.println("分支线程在执行--->" + i); } }); t.start(); for(int i = 0; i < 100; i++){ if(i % 20 == 0){ System.out.println("主线程让一次!"); } System.out.println("main线程在执行--->" + i); } try { t.join(); // join方法也是一种阻塞的方式 } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("main线程结束!"); } }
运行结果:
从上面截图可以看到有join方法后main的开始和结束就完全分开了
五、思路图一张
感谢大家的阅读,如有错误还望指正,谢谢^_^