day006-多线程
Posted gzyzhui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day006-多线程相关的知识,希望对你有一定的参考价值。
1、 线程概念
a) 什么是主线程
Java程序启动过程中自动创建的并执行main方法的线程称为主线程
- 主线程的执行路径:
从main方法开始到main方法结束
b)什么是子线程
除了主线程的其它所有线程都是子线程。
- 子线程的执行路径:
从run方法到run方法结束
C)线程的运行模式
- 分时式模式:每个线程平均分配CPU使用权,每一个线程使用CPU的时间是相同的。
- 抢占式模式:优先级高的线程抢到CPU的概率高,如果优先级都相同,就是随机抢占cpu的使用权
- Java程序的线程运行模式属于抢占式模式
2、 多线程内存图解
三句重要的小结:
每一个线程都会有自己独立的栈空间
进程的堆空间是被该进程的所有线程共享
在同一个线程中,代码是按顺序从上向下执行的。
3、 创建线程的两种方式
3.1创建一个类继承Thread类
重写run方法:将线程任务写在run()方法体内
调用start()方法,开启线程干活
例子:
1 package com.yangzhihui.level03.test02; 2 3 import java.util.Random; 4 5 public class ThreadDemo { 6 public static void main(String[] args) { 7 MyThread myThread01 = new MyThread("线程1:"); 8 MyThread myThread02 = new MyThread("线程2:"); 9 myThread01.start(); 10 myThread02.start(); 11 } 12 } 13 14 class MyThread extends Thread { 15 public MyThread(String name) { 16 super(name); 17 } 18 19 @Override 20 public void run() { 21 Random random = new Random(); 22 23 int sum = 0; 24 for (int i = 0; i < 30; i++) { 25 int num; 26 num = random.nextInt(901) + 100; 27 sum += num; 28 System.out.println(Thread.currentThread().getName() + num); 29 } 30 31 System.out.println("10个100~1000随机数的和是:" + Thread.currentThread().getName() + sum); 32 } 33 }
3.2 实现Runnable
- 步骤:
A)创建一个类实现Runnable接口,重写方法:将线程任务相关的代码写在run方法中
B) 创建实现类对象,根据实现类对象创建Thread对象
C) 调用线程对象的start方法开启线程:会在新的路径中执行run方法
- 使用Runnable接口的好处:
屏蔽了Java类单继承的局限性
可以更好的在多线程之间共享数据
将线程和任务进行分离,降低了程序的耦合性
为线程池提供前提条件
- 匿名内部类
什么时候使用匿名内部类创建线程
当任务只需要执行一次时,可以考虑使用匿名内部类
例子:
1 package level01.test03; 2 3 class MyRunnable03 implements Runnable { 4 @Override 5 public void run() { 6 System.out.println("实现接口方式:子线程的名称--" + Thread.currentThread().getName()); 7 } 8 } 9 10 11 package level01.test03; 12 13 public class TestMain { 14 public static void main(String[] args) { 15 //打印主线程的名称 16 System.out.println(Thread.currentThread().getName()); 17 18 19 //通过Runnable接口实现类对象,开启多线程 20 MyRunnable03 myRunnable03 = new MyRunnable03(); 21 new Thread(myRunnable03, "线程2").start(); 22 } 23 }
4、 线程安全
4.1 概念
指两个或两个以上的线程在同时操作一个共享资源时仍然得到正确的结果则就是线程安全。
- 线程安全的案例
火车站买票案例
4.2实现线程安全的方式
4.2.1:同步代码块
同步:每一个线程按顺序执行
异步:可以同时执行(多线程的代名词)
同步代码块格式:
Synchronized(锁对象){
//操作共享资源的代码
}
同步代码块的原理:
能够保证同一时间只有一个对象操作共享资源的代码
锁对象注意事项:
锁对象可以使任意类型的对象。
所有线程必须共用一把锁
例子:
1 package level01.test06; 2 3 public class Cup implements Runnable{ 4 private int sellNum = 0; 5 private final int NUM = 100; 6 private String cupLock = "lock"; 7 @Override 8 public void run() { 9 while(true){ 10 synchronized (cupLock){ 11 if(sellNum < NUM){ 12 try { 13 Thread.sleep(20); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 System.out.println(Thread.currentThread().getName() + "卖出第" + (++sellNum) + 18 "个,总共剩余" + (NUM - sellNum)); 19 }else { 20 System.out.println(Thread.currentThread().getName() + "买完了"); 21 break; 22 } 23 } 24 } 25 } 26 } 27 28 29 package level01.test06; 30 31 public class TestMain { 32 public static void main(String[] args) { 33 Cup cup = new Cup(); 34 35 new Thread(cup, "实体店").start(); 36 new Thread(cup, "官网").start(); 37 } 38 }
4.2.2 同步方法
同步方法格式:
修饰符 synchronized
返回值类型 方法名(参数列表){…}
同步方法:
能够保证同一时间,只有一个线程执行方法体内的代码
同步方法的注意事项
- 静态同步方法锁对象是:类名.class
附录:每一个类都会有一个Class对象,而且是唯一的。
Class c1 = TicketThread.class;
Class c2 = TicketThread.class;
- 非静态同步方法锁对象是:this
- 静态方法和非静态方法的选择
当方法体内部需要访问到任何非静态成员时,可以定义为静态方法。否则定义为非静态方法。
例子:
1 package level01.test07; 2 3 public class Bus implements Runnable { 4 private final int NUM = 80; 5 private int seatNum = 0; 6 7 @Override 8 public void run() { 9 while(seatNum < NUM){ 10 aboardBus(); 11 } 12 System.out.println("车座已满"); 13 } 14 15 public synchronized void aboardBus(){ 16 if(seatNum < NUM){ 17 try { 18 Thread.sleep(100); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 seatNum++; 23 System.out.println("从" + Thread.currentThread().getName() + 24 "上车,剩下" + (NUM - seatNum) + "座位"); 25 } 26 } 27 } 28 29 30 package level01.test07; 31 32 import java.io.BufferedWriter; 33 34 public class TestMain { 35 public static void main(String[] args) { 36 Bus bus = new Bus(); 37 38 new Thread(bus, "前门").start(); 39 new Thread(bus, "中门").start(); 40 new Thread(bus, "后门").start(); 41 } 42 }
4.2.3 锁
Lock接口:提供了比synchronized代码块和synchronized方法更广泛的锁操作,同步代码块/同步方法具有的功能Lock都有,除了之外更强大,更体现面向对象。
- 常用方法:
lock():上锁
unlock():释放锁
- Lock使用注意事项
lock()和unlock()必须成对出现。一定要 注意在线程执行完共享代码后,要释放锁。
例子:
1 package level01.test08; 2 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class Ticket implements Runnable{ 6 private final int NUM = 20; 7 private int seatNum = 0; 8 private ReentrantLock seatLock = new ReentrantLock(); 9 10 @Override 11 public void run() { 12 while(seatNum < 20){ 13 seatLock.lock(); 14 15 try { 16 if(seatNum < NUM){ 17 Thread.sleep(100); 18 seatNum++; 19 System.out.println("当前窗口:" + Thread.currentThread().getName() + 20 "卖了一张牌,剩余票数为" + (NUM - seatNum)); 21 }else { 22 System.out.println(Thread.currentThread().getName() + "票卖完了"); 23 } 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 }finally { 27 seatLock.unlock(); 28 } 29 } 30 } 31 } 32 33 34 package level01.test08; 35 36 public class TestMain { 37 public static void main(String[] args) { 38 Ticket ticket = new Ticket(); 39 new Thread(ticket,"线程1:").start(); 40 new Thread(ticket,"线程2:").start(); 41 new Thread(ticket,"线程3:").start(); 42 new Thread(ticket,"线程4:").start(); 43 } 44 }
5、 线程状态
NEW(新建):新建状态,刚刚创建出来,还没有调用start方法
Runnable(可运行):可运行状态,有资格运行,可能正在运行中,也可以不是正在运行
Blocked(锁阻塞):等待其他线程释放锁对象
Wating(无限等待):无限等待
TimedWating(计时等待):限时等待,调用了sleep方法或者wait(毫秒数),需要被其他线程调用notify方法唤醒
Teminated(被终止):死亡状态,任务执行完毕或调用了stop方法
以上是关于day006-多线程的主要内容,如果未能解决你的问题,请参考以下文章