多线程
1. 进程
进行中的应用程序,计算机分配资源的最小单位
只有程序在运行状态下,才称之为进程
2. 线程
线程的包含在进程之中的,属于计算机运算执行的最小单位
一个进程至少包含一个线程,否则无法运行
3. 线程执行
多线程在单个核心的CPU下是随机轮流交替执行的
线程的执行存在不确定性
线程的执行决定权在CPU,不在线程
4. 并发和并行
并发:是指同时发生,轮流交替执行
并行:真正意义上的同时执行
5. 线程名称
通过setName设置名称
通过getName获取名称
main方法所属线程名默认为main
package com.qfedu.test1;
/**
* 获取当前线程名称
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName()); // 获取线程名字
currentThread.setName("hello world"); // 设置线程名字
System.out.println(currentThread.getName());
}
}
6. 创建线程的方式
6.1 继承Thread类
1.继承Thread类
2.重写run方法
启动线程调用start方法
package com.qfedu.test1;
/**
* 实现多线程的方式1:继承Thread类
* @author WHD
*
*/
public class Test2 extends Thread{
/**
* run方法中代码为我们当前线程要执行的逻辑代码
* 但是代码的执行决定权不在线程 在CPU
*/
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
Test2 th1 = new Test2();
th1.setName("线程A");
th1.start(); // 向CPU发出准备就绪信号 等待被执行
Test2 th2 = new Test2();
th2.setName("线程B");
th2.start();
}
6.2 实现Runnable接口
1.实现Runnable接口
2.重写run方法‘
Runnable实现类可作为参数构造Thread实例
package com.qfedu.test2;
/**
* 创建线程方式2:实现Runnable接口
* @author WHD
*
*/
public class Test1 implements Runnable{
int num = 10;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程名称:" + Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
Test1 test1 = new Test1();
Thread th1 = new Thread(test1, "线程A");
Thread th2 = new Thread(test1, "线程B");
th1.start();
th2.start();
}
}
7. 两种创建方式区别?
继承Thread类
编写简单,可直接操作线程 适用于单继承实现Runnable接口
避免单继承局限性 便于共享资源推荐使用实现Runnable接口方式创建线程
8. 面试题:调用start和run方法的区别?
因为线程执行的决定权不在线程,而在CPU,所以线程只能向CPU发出准备就绪信号,等待被执行
所以调用start方法,就是发出信号,等待CPU开启新的线程
调用run方法,不会开启新的线程
**调用start开启新线程,调用run不会开启新线程**
9. 线程的状态
package com.qfedu.test2;
/**
* 线程的状态
* @author WHD
*
*/
public class Test2 extends Thread{
@Override
public void run() { // 运行
System.out.println("开始执行run方法");
try {
Thread.sleep(3000); // 阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名字:" + Thread.currentThread().getName());
} // 死亡
public static void main(String[] args) {
Test2 th1 = new Test2(); // 创建状态
th1.start(); // 就绪
}
}
10. 线程调度常用方法
10.1. 线程优先级
默认为5 最高为10 最低为1
package com.qfedu.test3;
/**
* 线程优先级
* 默认为5 最高为10 最低为1
* @author WHD
*
*/
public class Test1{
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
System.out.println(mainThread.getPriority());
mainThread.setPriority(Thread.MAX_PRIORITY);
System.out.println(mainThread.getPriority());
}
}
package com.qfedu.test3;
public class Test2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
Test2 th1 = new Test2();
th1.setPriority(Thread.MAX_PRIORITY);
th1.setName("----线程A----");
Test2 th2 = new Test2();
th2.setPriority(1);
th2.setName("线程B");
th1.start();
th2.start();
}
}
10.2 线程休眠
sleep(long millis) 线程休眠方法
10.3 线程插队
插队 join
一个线程插队另外一个线程
join() 等待插队线程执行完 被插队线程再执行
join(int millis) 等待插队线程指定时间 时间一到 继续轮流交替
package com.qfedu.test3;
/**
* 插队 join
* 一个线程插队另外一个线程
*
* join() 等待插队线程执行完 被插队线程再执行
* join(int millis) 等待插队线程指定时间 时间一到 继续轮流交替
* @author WHD
*
*/
public class Test3 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
Test3 th1 = new Test3();
th1.setName("线程A");
th1.start();
for (int i = 0; i < 50; i++) {
if(i == 10) {
try {
th1.join(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "*********" + i);
}
}
}
10.4 线程礼让
yield 礼让
礼让不能够保证一定会礼让
package com.qfedu.test3;
/**
* yield 礼让
* 礼让不能够保证一定会礼让
* @author WHD
*
*/
public class Test4 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i == 10) {
System.out.println("---------线程礼让---------");
Thread.yield();
}
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Test4 th1 = new Test4();
th1.setName("线程A*****");
Test4 th2 = new Test4();
th2.setName("线程B");
th1.start();
th2.start();
}
}
11. 练习题
11.1 模拟爬山
爬山类
每个线程代表一个人
可设置每人爬山速度
每爬完100米显示信息
爬到终点时给出相应提示
package com.qfedu.test4;
/**
* 爬山类
* 每个线程代表一个人
* 可设置每人爬山速度
* 每爬完100米显示信息
* 爬到终点时给出相应提示
*
*
* @author WHD
*
*/
public class ClimbThread extends Thread{
int time; // 爬100耗时
int length; // 总长度 赋值1000
@Override
public void run() {
while(length > 0) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
length -= 100;
System.out.println(Thread.currentThread().getName() + "爬了100米,还剩余" + length);
}
System.out.println("恭喜:" + Thread.currentThread().getName() + "爬到了山顶");
}
public ClimbThread(int time,int length,String name) {
super(name);
this.time = time;
this.length = length;
}
public static void main(String[] args) {
ClimbThread youngMan = new ClimbThread(500, 1000, "练习两年半的实习生");
ClimbThread oldMan = new ClimbThread(1000, 1000, "老年人");
youngMan.start();
oldMan.start();
}
}
11.2 模拟看病
某科室一天需看普通号50个,特需号10个
特需号看病时间是普通号的2倍
开始时普通号和特需号并行叫号,叫到特需号的概率比普通号高
当普通号叫完第10号时,要求先看完全部特需号,再看普通号
使用多线程模拟这一过程
思路:创建当前类为特需号类 ,main方法中main线程作为普通号
package com.qfedu.test4;
/**
* 某科室一天需看普通号50个,特需号10个
* 特需号看病时间是普通号的2倍
* 开始时普通号和特需号并行叫号,叫到特需号的概率比普通号高
* 当普通号叫完第10号时,要求先看完全部特需号,再看普通号
* 使用多线程模拟这一过程
*
* 思路:创建当前类为特需号类 ,main方法中main线程作为普通号
* @author WHD
*
*/
public class Special extends Thread{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i + "号在看病");
}
System.out.println("特需号看病完成*********************************");
}
public static void main(String[] args) throws InterruptedException {
Special s = new Special();
s.setName("特需号");
s.setPriority(Thread.MAX_PRIORITY);
s.start();
Thread.currentThread().setName("普通号");
for (int i = 1; i <= 50; i++) {
if(i == 11) {
s.join();
}
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + i + "号在看病");
}
System.out.println("普通号看病完毕###############");
}
}
12.synchronized 关键字
synchronized:同步
应用场景:
1.修饰代码块 表示同一时间只能有一个线程访问此代码块
2.修饰方法 同一时间只能有一个线程访问此方法
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
package com.qfedu.test5;
/**
* 使用多线程模拟买票的操作
* 三个线程(三个人)抢10张票,必须保证:
* 1.票号不能重复
* 2.不能超卖
*
*
* 出现的问题:会出现重复的票号 和 出现超卖的问题 都是因为
* 循环的判断和 数据的修改 没有绑定在一起 所以解决方案:将条件判断和数据修改 绑定在一起(上锁)
*
* synchronized:同步
* 应用场景:
* 1.修饰代码块 表示同一时间只能有一个线程访问此代码块
* 2.修饰方法 同一时间只能有一个线程访问此方法
* @author WHD
*
*/
public class BuyTicket3 implements Runnable{
int count = 10;
Object obj = new Object();
@Override
public void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) { // 这里不是必须写this 但是必须保证多个线程锁定的是同一个对象 才有同步的效果
if(count == 0) {
break;
}
count --;
System.out.println(Thread.currentThread().getName() + "抢到第"+ (10 - count) +"张票,还剩余" + count + "张票");
}
}
}
public static void main(String[] args) {
BuyTicket3 buyTicket = new BuyTicket3();
Thread th1 = new Thread(buyTicket, "赵四");
Thread th2 = new Thread(buyTicket, "广坤");
Thread th3 = new Thread(buyTicket, "大拿");
th1.start();
th2.start();
th3.start();
}
}
package com.qfedu.test5;
/**
* 使用多线程模拟买票的操作
* 三个线程(三个人)抢10张票,必须保证:
* 1.票号不能重复
* 2.不能超卖
*
*
* 出现的问题:会出现重复的票号 和 出现超卖的问题 都是因为
* 循环的判断和 数据的修改 没有绑定在一起 所以解决方案:将条件判断和数据修改 绑定在一起(上锁)
*
* synchronized:同步
* 应用场景:
* 1.修饰代码块 表示同一时间只能有一个线程访问此代码块
* 2.修饰方法 同一时间只能有一个线程访问此方法
* @author WHD
*
*/
public class BuyTicket4 implements Runnable{
int count = 10;
Object obj = new Object();
@Override
public synchronized void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 0) {
break;
}
count --;
System.out.println(Thread.currentThread().getName() + "抢到第"+ (10 - count) +"张票,还剩余" + count + "张票");
}
}
public static void main(String[] args) {
BuyTicket4 buyTicket = new BuyTicket4();
Thread th1 = new Thread(buyTicket, "赵四");
Thread th2 = new Thread(buyTicket, "广坤");
Thread th3 = new Thread(buyTicket, "大拿");
th1.start();
th2.start();
th3.start();
}
}