一、目标
- 理解线程的概念
- 掌握线程的创建和启动
- 了解线程的状态
- 掌握线程调度的常用方法
- 掌握线程的同步
- 理解线程安全的类型
二、进程、线程、多线程的理解
进程:应用程序的执行实例、有独立的内存空间和系统资源
线程:CPU调度和分派的基本单位、进程中执行运算的最小单位,可完成一个独立的顺序控制流程
进程由多个线程组成
什么是多线程
如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程” 多个线程交替占用CPU资源,而非真正的并行执行
多线程好处
充分利用CPU的资源 简化编程模型 带来良好的用户体验
三、主线程
- main()方法即为主线程入口
- 产生其他子线程的线程
- 必须最后完成执行,因为它执行各种关闭动作
public static void main(String args[]) { Thread t= Thread.currentThread(); System.out.println("当前线程是: "+t.getName()); t.setName("MyJavaThread"); System.out.println("当前线程名是: "+t.getName()); }
四、线程的创建和启动
在Java中创建线程的两种方式
- 继承java.lang.Thread类
public class MyThread extends Thread{ @Override public void run() { //循环输出1-20之间的所有的整数 for (int i = 1; i < 21; i++) { System.out.println(Thread.currentThread().getName()+"正在运行"+i+"次"); } } } public class Test { public static void main(String[] args) { for (int i = 1; i < 21; i++) { System.out.println(Thread.currentThread().getName()+"这是循环的第"+i); } //创建自定义线程类的对象 MyThread myThread=new MyThread(); MyThread myThread1=new MyThread(); //修改线程名 myThread1.setName("线程B"); myThread.setName("线程A"); //启动线程 //通过线程直接调用run()方法,由main执行 /* myThread.run();*/ //通过线程调用start()方法来启动线程,是由子线程来执行myThread.start(); myThread.start(); myThread1.start(); } }
- 实现java.lang.Runnable接口
public class MyRunnable implements Runnable { @Override public void run() { //循环输出1-20之间所有的整数 for (int i = 1; i < 21; i++) { System.out.println(Thread.currentThread().getName()+"正在运行"+i+"次"); } } } public class Test { public static void main(String[] args) throws InterruptedException { MyRunnable myRunnable=new MyRunnable(); //启动线程 /*myRunnable.run();*/ //myRunnable类中没有start()方法,他实现的接口也没有start()方法,因为start()方法是Thread中的 Thread thread1=new Thread(myRunnable,"吴子涵"); Thread thread2=new Thread(myRunnable,"吴狗"); /* //修改线程名字 thread1.setName("吴子涵"); thread2.setName("吴狗");*/ //修改线程优先级 /* thread1.setPriority(1); thread2.setPriority(10);*/ thread1.start(); thread2.start(); } }
使用线程的步骤
- 定义线程
- 创建线程对象
- 启动线程
- 终止线程
以上两种创建方式的区别
- 继承Thread类
- 编写简单,可直接操作线程
适用于单继承
- 实现Runnable接口
- 避免单继承局限性 便于共享资源
五、线程的状态
六、线程的调度(各种方法的使用)
1、优先级:void setPriority(int newPriority)
//设置优先级 thread1.setPriority(1); thread2.setPriority(10);
wait() 方法需要在一个同步块或同步方法中被调用,这是因为 wait() 方法会释放对象锁,使得其他线程可以进入同步块或同步方法,获取该对象的锁并执行,如果不在同步块或同步方法中调用 wait(),将会抛出 IllegalMonitorStateException 异常。当一个线程调用了对象的 wait() 方法后,该线程将会进入等待状态,直到其他线程调用了该对象的 notify() 或 notifyAll() 方法,在这期间,该线程会释放对象锁,使得其他线程可以获取该对象的锁并执行。
2、休眠:static void sleep(long millis)
将一个线程休眠,停止运行,cpu可以被其他线程占用
public class SleepDemo01 { public static void main(String[] args) throws InterruptedException { System.out.println("线程开始执行"); //线程休眠5秒 Thread.sleep(5000); System.out.println("线程结束执行"); } }
3、强制运行:void join()
强制运行一个线程,等待这个线程运行完成之后,其他线程才能去抢占cpu
public class Test { public static void main(String[] args) throws InterruptedException { //创建线程 MyRunnable myRunnable=new MyRunnable(); Thread thread=new Thread(myRunnable); // thread.setPriority(10); thread.start(); for (int i = 10; i < 100; i+=10) { System.out.println(Thread.currentThread().getName()+"运行的次数是"+i+"次"); // 强制执行线程 会暂停当前正在运行的线程main 当强制执行完了,暂停执行的当前线程会继续执行 if(i==50){ thread.join(); } } } }
4、礼让:static void yield()
当一个线程抢到cpu资源之后不运行,主动退出,然后再次和其他线程去争抢cpu资源
public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 1; i <10 ; i++) { System.out.println(Thread.currentThread().getName()+"正在运行第"+i+"次"); if(i==3){ System.out.print("线程礼让"); Thread.yield(); } } } }
七、多线程共享数据解决方法(同步方法)
使用synchronized修饰的方法控制对类成员变量的访问
- 访问修饰符 synchronized 返回类型 方法名(参数列表){……}
- synchronized 访问修饰符 返回类型 方法名(参数列表){……}
public class Site implements Runnable { private int num = 10; // 购买到的是第几张票 private int count = 0; @Override public void run() { while (true){ if (!sale()){ break; } } } //定义一个同步方法,实现卖票 public synchronized boolean sale() { if (num <= 0) { return false; } num--; count++; // 模拟网络延迟 /* try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }*/ // 输出一下卖票信息,看一下是哪个线程的用户购买了票 System.out.println(Thread.currentThread().getName() + "购买了的第" + count + "张票剩余" + num + "张票"); return true; } }
使用synchronized关键字修饰的代码块
public void run() { while (true) { synchronized (this) { //同步代码块 // 省略修改数据的代码...... // 省略显示信息的代码...... }}}
public class Site implements Runnable { // 定义两个属性用来统计票的数目和买到的是第几张票 // 票剩余的数量 private int num = 10; // 购买到的是第几张票 private int count = 0; @Override public void run() { while (true) { // 票卖完了就可以结束这个循环 synchronized (this) { if (num <= 0) { break; } num--; count++; // 模拟网络延迟 try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } // 输出一下卖票信息,看一下是哪个线程的用户购买了票 System.out.println(Thread.currentThread().getName() + "购买了的第" + count + "张票剩余" + num + "张票"); } } } }
八、线程安全的类型