多线程
1.进程
进行中的应用程序,只有一个应用程序处于运行状态,才能被称之为进程。
应用程序的执行实例,有独立的内存空间和系统资源。
2.线程
CPU执行的最小单位,包含在进程中。
CPU调度和分派的基本单位,应用程序运算是最小单位。
3.进程和线程的关系
进程和线程的关系,就像车身和车轮的关系,不是越多越好,要根据实际的硬件环境,选择最合适的数量。
4.CPU和线程的执行
单核CPU下,多线程是轮流交替执行。每个任务最多执行20ms,每个任务准备好以后,会向CPU发出指令:准备好了
真正是否能够被执行,不确定,因为同时可能有很多线程都准备好了
5.并发和并行
并发是指同时发生,轮流交替来执行
并行是真正意义上的同时执行
6.线程的创建
6.1继承Thread类
线程的创建方式1:继承Thread类 重写run方法
package com.qfedu.test2;
/**
* 线程的创建方式1:继承Thread类 重写run方法
* @author WHD
*
*/
public class T1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
T1 t1 = new T1();
t1.setName("线程A");
t1.start(); // 调用start方法才会开启新的线程
// t1.run(); 调用run方法不会开启新的线程 依然使用main线程调用run方法
}
}
6.2实现Runnable接口
创建线程方式2:实现Rruuable接口,重写run方法
package com.qfedu.test2;
/**
* 创建线程方式2 :实现Runnable接口 重写run方法
* @author WHD
*
*/
public class T2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
T2 t2 = new T2();
Thread thread = new Thread(t2, "线程A");
thread.start();
}
}
6.3两种方式对比
继承Thread类
编写简单,可直接操作线程
适用于单继承
实现Runnable接口
避免单继承局限性
便于共享资源
推荐使用实现runnable接口的方式创建线程
7.线程的状态
线程的状态
创建--就绪-- 阻塞--死亡
package com.qfedu.test3;
/**
* 线程的状态
* 创建-->就绪-->运行-->阻塞-->死亡
* @author WHD
*
*/
public class T1 extends Thread{
@Override
public void run() { // 运行
try {
Thread.sleep(2000); // 阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
// 死亡
public static void main(String[] args) {
T1 t1 = new T1(); // 创建
t1.setName("线程A");
t1.start(); // 就绪
}
}
8.线程的优先级
线程的优先级,默认为5,最低为1,最高位10
优先级高代表获取CPU的概率较大,并不能保证一定会优先获得CPU资源
MAX_PRIORITY最高10
MIN_PRIORITY最低1
package com.qfedu.test4;
/**
* 线程的优先级 默认为5 最低为1 最高为10
* 优先级高代表获取CPU的概率较大 并不能保证一定会优先获得CPU资源
* @author WHD
*
*/
public class T1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
T1 thread1 = new T1();
thread1.setName("赵四");
T1 thread2 = new T1();
thread2.setName("广坤");
System.out.println(thread1.getPriority());
System.out.println(thread2.getPriority());
thread1.setPriority(Thread.MAX_PRIORITY); // 设置优先级为最高 10
thread2.setPriority(1);// 设置优先级为最低 1
thread1.start();
thread2.start();
}
}
9.线程的休眠
线程休眠的方法sleep(long mills)
package com.qfedu.test5;
/**
* 线程休眠的方法 sleep(long mills)
*
* @author WHD
*
*/
public class T1 extends Thread{
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
T1 t1 = new T1();
t1.setName("线程A");
t1.start();
}
}
10.线程的插队
线程的插队
join()等待插队线程执行完毕,再执行当前线程
join(long mills)等待插队线程固定时间,时间就执行当前线程
package com.qfedu.test5;
/**
* 线程的插队
* join() 等待插队线程执行完毕 再执行当前线程
* join(long mills) 等待插队线程固定时间 时间结束就执行当前线程
* @author WHD
*
*/
public class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.setName("--------线程A--------");
t2.start();
for (int i = 0; i < 20; i++) {
if(i == 10) {
t2.join(1000);
}
System.out.println(Thread.currentThread().getName() + i);
}
}
}
11.线程的礼让
yield()方法,线程礼让,当前线程礼让其他线程,让其他线程先执行
只是提供一种可能,不一定会礼让
package com.qfedu.test6;
/**
* yield()方法 线程礼让 当前线程礼让其他线程 让其他线程先执行
* 只是提供一种可能 不一定会礼让
* @author WHD
*
*/
public class T1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i == 5) {
Thread.yield();
System.out.println(Thread.currentThread().getName() + "礼让了------------------");
}
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
T1 thread1 = new T1();
T1 thread2 = new T1();
thread1.setName("线程A");
thread2.setName("线程B");
thread1.start();
thread2.start();
}
}
12.线程的中断
interrupt()中断线程的方法,但不是立即中断,如果当前线程有未执行完的任务,将执行完毕再中断
inerrupted()查看当前线程是否可以被中断,true为是,false为否
stop()为立即中断线程的方法
package com.qfedu.test6;
/**
* interrupt() 中断线程方法 但不是立即中断 如果当前线程有未执行完的任务 将执行完毕再中断
* interrupted() 查看当前线程是否可以被中断 true为是 false为否
* stop() 为立即中断线程的方法
* @author WHD
*
*/
public class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i == 10) {
System.out.println("执行了线程中断方法interrupt()");
Thread.currentThread().interrupt();
// boolean flag = Thread.interrupted();
// System.out.println(flag);
}
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.setName("线程A");
t2.start();
Thread.sleep(2000);
System.out.println(t2.isAlive()); // 查看t2线程是否存活
}
}
13.线程安全
多线程共享数据引发的问题
package com.qfedu.test8;
/**
* 因为T1类中count是实例类型的 所以每new一个对象 就存在一份拷贝 有三个count = 10
* 所以我们将count改为static修饰的
* 修改完以后:
* 1.同一张票重复卖出
* 2.在run方法中添加线程休眠 发现票的数量有误
* @author WHD
*
*/
public class T2 extends Thread{
private static int count = 10;
@Override
public void run() {
while(count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");
}
}
public static void main(String[] args) {
T2 zhaosi = new T2();
T2 guangkun = new T2();
T2 dana = new T2();
zhaosi.setName("赵四");
guangkun.setName("广坤");
dana.setName("大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
解决方案:使用同步代码块
package com.qfedu.test8;
/**
*
* @author WHD
*
*/
public class T4 implements Runnable{
int count = 10;
@Override
public void run() {
while(true ) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(new Object()) {
if(count == 0) {
break;
}
count--;
System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");
}
}
// 后续代码
}
public static void main(String[] args) {
T4 t3 = new T4();
Thread zhaosi = new Thread(t3, "赵四");
Thread guangkun = new Thread(t3, "广坤");
Thread dana = new Thread(t3, "大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
使用同步方法实现
package com.qfedu.test8;
/**
* synchronized修饰方法 表示此方法同一时间只能有一个线程访问
* @author WHD
*
*/
public class T5 implements Runnable{
int count = 10;
@Override
public synchronized void run() {
while(count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");
}
// 后续代码
}
public static void main(String[] args) {
T5 t3 = new T5();
Thread zhaosi = new Thread(t3, "赵四");
Thread guangkun = new Thread(t3, "广坤");
Thread dana = new Thread(t3, "大拿");
zhaosi.start();
guangkun.start();
dana.start();
}
}
14.synchronized关键字规则
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码