多线程
多线程
进程、程序、线程
- 程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
- 进程:是执行程序的一次执行过程,它是一个动态的概念。是系统分配的单位
- 线程:通常在一个进程中额可以包含多个线程,当然一个进程中至少有一个线程,线程是CPU调度和执行的单位。
- 注意:线程开启不一定马上执行,由CPU调度
注意:很多多线程都是模拟出来的,真正的多线程要调用多个CPU
多线程的创建方式
- thread class
- 继承 Thread 类(重点)
- TestThread1 --- 简单展示多线程的特点
- Runnable 接口
- 实现 Runnable 接口(重点)
- TestThread2 --- 简单展示
- Callable 接口
- 实现 Callable 接口(了解)
关于实现多线程的几种方法
继承Thread类
- 子类继承Thread具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
静态代理模式
- 例子 :demo1.StaticProxy
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
- 真实对象专注自己的事情
- 其他事情交付给代理对象实现
Lambda 表达式
函数式接口
- 任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口
- 对于函数式接口可以通过lambda表达式来创建该接口的对象
- 是使用lambda表达式的前提
小例子
package learnSpace.lambda;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class demo1 {
public static void main(String[] args) {
new Like().lambda();
ILike like = ()->{
System.out.println("lambda coming");
};
like.lambda();
}
}
interface ILike{
void lambda();
}
class Like implements ILike{
@Override
public void lambda() {
System.out.println("test lambda");
}
}
简化写法
- 简化赋值括号和花括号
- 但是如果逻辑有多行的时候就不能简化{}了
- 同样如果有多个参数就不能省略()了,但是仍然可以省略参数类型
ILove t;
t = (int a)->{
System.out.println("Test a = " + a);
};
t = a -> System.out.println("Simply a = " + a);
/*
t = (a,b)->{
System.out.println("Simply a = " + a);
System.out.println("Simply b = " + b);
}
*/
线程状态
线程停止
- 建议线程正常停止 ---> 利用次数,不建议死循环
- 建议使用标志位 ---> 设置一个标志位
- 不要使用 stop 、 destroy 等过时或 JDK 不建议使用的方法
- 小例子
package learnSpace.statue;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestStop implements Runnable{
//1. 设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("running... i = " + i ++);
}
}
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop t = new TestStop();
new Thread(t).start();
for(int i = 0;i < 1000;i ++){
System.out.println("In main, i = " + i);
if(i == 900){
t.stop();
System.out.println("Stop!");
}
}
}
}
线程休眠
- sleep(int miles);
- 指定当前线程阻塞的毫秒数
- sleep 时间达到后线程进入就绪状态
- sleep 可以模拟网络延时、倒计时等
- 每个对象都有一个锁,sleep 不会释放锁
线程礼让
- 让当前执行的线程暂停,但不阻塞
- 将线程从以女性状态转为就绪状态
- 让CPU重新调度,礼让不一定成功! > 看CPU如何调度,礼让不一定成功
线程强制执行
-
Join 合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
-
可以理解成插队
-
小例子
package learnSpace.statue;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestJoin implements Runnable{
@Override
public void run() {
for(int i = 0;i < 100;i ++){
System.out.println("vip coming... " + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin t = new TestJoin();
Thread thread = new Thread(t);
thread.start();
for(int i = 0;i < 50;i ++){
if(i == 30){
thread.join();
System.out.println("Join!!");
}
System.out.println("main... " + i);
}
}
}
线程状态观测实例
package learnSpace.statue;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("...");
});
//观察状态
System.out.println(t.getState() + " before start");
t.start();
System.out.println(t.getState() + " after start");
while(t.getState() != Thread.State.TERMINATED){
Thread.sleep(100);
System.out.println(t.getState());
}
}
}
线程的优先级
- 观测实例
package learnSpace.statue;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestPriority{
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());
MyPriority t = new MyPriority();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
Thread t6 = new Thread(t);
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
t5.setPriority(7);
t5.start();
t6.setPriority(8);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " --> " + Thread.currentThread().getPriority());
}
}
- 注意:并非优先级高的就一定先跑,还是根据CPU调度来决定先后顺序的,优先级高只是被调度的概率高
守护线程
- 一个不需要结束的线程
- 实例
package learnSpace.statue;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);
//守护线程不需要停止
thread.start();
new Thread(you).start();
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("happy living...");
}
System.out.println("bye");
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("God's watching");
}
}
}
线程同步
-
多个线程操作同一个资源
-
形成条件:队列 + 锁
-
并发问题:同一个对象被多个线程同时操作
-
处理并发问题的时候就需要线程同步,多个需要访问的线程进入对象的等待池形成队列
队列和锁
- 队列:解决并发问题形成的线程队列
- 锁:上一个线程执行完毕就开锁,让下一个线程进来执行
使用锁会造成的问题
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 多线程竞争下,锁的操作会消耗额外的资源引起性能问题
- 优先级高的线程等待优先级低的线程释放锁,会导致优先级倒置,引起性能问题
同步方法
- 方法中需要修改的内容才需要锁,锁的太多会浪费资源
同步块
小总结
- 同步方法锁的是this
- 同步块锁的是会被修改的类
线程安全的JUC集合类型
package learnSpace.syn;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
-
两个或多个线程都在等对方释放自己需要的资源,造成程序停止运行
-
小例子
package learnSpace.thread;
/**
* @Author: Xuuxxi
* @Date: 2022/4/1
*/
public class DeadLock {
public static void main(String[] args) {
Makeup a = new Makeup(0, "A");
Makeup b = new Makeup(1, "B");
a.start();
b.start();
}
}
class Lipstick{}
class Mirror{}
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
//死锁警告
/*
if(choice == 0){
synchronized (lipstick){
System.out.println("lipstick's lock " + Thread.currentThread().getName());
Thread.sleep(1000);
synchronized (mirror){
System.out.println("mirror's lock " + Thread.currentThread().getName());
}
}
}else{
synchronized (mirror){
System.out.println("mirror's lock " + Thread.currentThread().getName());
Thread.sleep(2000);
synchronized (lipstick){
System.out.println("lipstick's lock " + Thread.currentThread().getName());
}
}
}
*/
//不抱住对方的锁就不会产生死锁现象
if(choice == 0){
synchronized (lipstick){
System.out.println("lipstick's lock " + Thread.currentThread().getName());
Thread.sleep(1000);
}
synchronized (mirror){
System.out.println("mirror's lock " + Thread.currentThread().getName());
}
}else{
synchronized (mirror){
System.out.println("mirror's lock " + Thread.currentThread().getName());
Thread.sleep(2000);
}
synchronized (lipstick){
System.out.println("lipstick's lock " + Thread.currentThread().getName());
}
}
}
}
- 死锁形成原因
Lock 锁
- 给线程加锁的方法
- 实例
package learnSpace.highLevel;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: Xuuxxi
* @Date: 2022/4/2
*/
public class TestLock {
public static void main(String[] args) {
TestClock2 t = new TestClock2();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestClock2 implements Runnable{
int num = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock(); //线程加锁
if(num > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num --);
}else{
break;
}
}finally {
lock.unlock();
}
}
}
}
和 synchronized 的差别
线程协作
线程通信
-
生产者和消费者共享一个资源
-
Java 提供的几种方法
- wait 线程等待唤醒
- notify 唤醒线程
-
注意 :都是 Object 类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常