文章目录
线程
概念介绍
CPU的核数:瞬时时间能处理的任务数
CPU的主频:频繁的切换以至于可以“同时”运行更多的程序
程序(program):为完成特定任务,用某种语言编写的一组指令的集合。即静态代码、静态对象。
进程(progress):正在运行的一个程序
线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。
多线程,一个进程(一个程序运行时),可以分化为并行执行多个线程(多个子程序)
进程相当于一条河,线程相当于河的分支。
何时需要多线程
程序需要同时执行两个或多个任务。
程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
需要一些后台运行的程序时。
多线程的实现
通过继承Thread实现
TestThread.java
/**
* 继承Thread的方式实现多线程
*
* @Author Nino 2019/10/3
*/
public class TestThread extends Thread {
@Override
public void run() {
System.out.println("多线程运行的代码");
for (int i = 0; i < 5; i++) {
System.out.println("这是多线程的逻辑代码:" + i);
}
}
}
Main
public static void main(String[] args) {
Thread thread = new TestThread();
thread.start();// 启动线程
System.out.println("---------------");
System.out.println("---------------");
System.out.println("---------------");
}
多线程的异步:
当main执行start方法开启多线程之后,相当于在main方法之外开启了一个支流,在这往后的main函数中的其他代码的运行就与run方法的运行无关了。
通过实现Runnable的接口实现
TestRunnable.java
/**
* 通过实现Runnable接口方式实现多线程
*
* @Author Nino 2019/10/3
*/
public class TestRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"Runnable多线程运行的代码");
for (int i = 0; i < 5; i++) {
System.out.println("这是Runnable多线程的逻辑代码:" + i);
}
}
}
main
public static void main(String[] args) {
Thread thread1 = new Thread(new TestRunnable(), "t-1");
thread1.start();
System.out.println("---------------");
System.out.println("---------------");
System.out.println("---------------");
}
以上两种实现方法的对比
区别:
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码放在实现接口的子类中。
共性:
都需要Thread父类来完成run和start操作
实际使用中更常用的是实现方法,好处如下:
- 避免了单继承的局限性
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
第二点可以看以下代码
TestRunnable.java
/**
* 通过实现Runnable接口方式实现多线程
*
* @Author Nino 2019/10/3
*/
public class TestRunnable implements Runnable {
int count = 0;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"Runnable多线程运行的代码");
for (int i = 0; i < 5; i++) {
count++;
System.out.println(Thread.currentThread().getName() + "这是Runnable多线程的逻辑代码:" + count);
}
}
}
Main.java
/**
* @Author Nino 2019/10/3
*/
public class Main {
public static void main(String[] args) {
Runnable run = new TestRunnable();
Thread thread = new Thread(run, "t-1");
Thread thread1 = new Thread(run, "t-2");
thread.start();
thread1.start();
}
}
输出:
t-2Runnable多线程运行的代码
t-1Runnable多线程运行的代码
t-2这是Runnable多线程的逻辑代码:1
t-1这是Runnable多线程的逻辑代码:2
t-2这是Runnable多线程的逻辑代码:3
t-1这是Runnable多线程的逻辑代码:4
t-2这是Runnable多线程的逻辑代码:5
t-1这是Runnable多线程的逻辑代码:6
t-2这是Runnable多线程的逻辑代码:7
t-1这是Runnable多线程的逻辑代码:8
t-2这是Runnable多线程的逻辑代码:9
t-1这是Runnable多线程的逻辑代码:10
可以明显看出两个线程在共享对象,处理同一资源。
使用多线程的优点
- 提高应用程序的响应,增强用户体验。
- 提高计算机系统CPU的利用率
- 改善程序结构,将长进程分为多个线程,独立运行。
Thread类有关的方法
void start();
run();
String getName();
void setName();
static currentThread();// 当前线程
线程的优先级
即哪个线程有较大的概率被执行
优先级只可以增大概率 ,并不一定是优先执行
优先级用数字分为[1, 10],数字越大,优先级越高,如果没有设置,默认优先级为5
方法:
int getPriority();
void setPriority(int);
//线程让步
//暂停当前执行中的线程,把机会让给优先级相同或更高的线程
//如没有这样的线程,则忽视
static void yield();
//阻塞其余线程,直到join进来的线程执行完为止
void join();
//睡眠
void sleep(long ?ms);
//强制结束线程生命周期
void stop();
//判断当前线程是否还活着
boolean isAlive();
线程的生命周期
-
新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
-
就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
-
运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
-
阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态
-
死亡状态,线程完成了全部工作或被提前强制中止
线程的同步
为什么要线程同步
- 多个线程执行的不确定性会引起执行结果的不稳定
- 多个线程对账本的共享,会造成操作的不完整性,会破坏数据
通过synchronized同步锁完成同步
public synchronized void xxx()
只需要直接在可能发生问题的方法上加上synchronized
关键字就可以。
注意:在普通方法上加同步锁synchronized
,锁的是整个对象,并不单单是这个方法,如果其他方法也加上同步锁,则共享同步锁。
不同的对象是不同的锁。
静态的方法加synchronized
将会共用一个锁。
对代码块加入同步锁
// 表示传进来的对象要被同步锁,一般是this
synchronized(对象){
}
用this锁代码块是代表当前的对象被锁,如果在其他方法中也有synchronized(this)
的代码块,使用的都是同一个异步锁。
要想不使用同一个异步锁,给synchronized(对象)
传递一个非this的不同对象即可。
线程的死锁问题
原因
- 当前线程拥有其他线程需要的资源
- 当前线程等待其他线程已拥有的资源
- 都不放弃自己拥有的资源
比如:
有线程a0,需要执行方法f0
有线程a1,需要执行方法f1
f0和f1都有同步锁的方法
现在发生了:
a0调用f1方法并且一直没有执行完f1
a1调用f0方法并且一直没有执行完f0
双方线程都在等对方释放方法,对方都不释放,形成死锁
解决方法
- 专门的算法,比如加锁的顺序一致
- 尽量减少同步资源的定义
线程通信
主要有以下几种方法,必须在synchronized
修饰的方法中或者代码块下才可用,不然会报异常。
void wait(); // 令当前线程暂时挂起并放弃CPU、同步资源,进入排队等待
void notify(); // 唤醒正在排队的优先级最高的线程,使其结束等待
void notifyAll(); // 唤醒正在排队等待资源的所有线程