多线程
1.并发与并行
并发:指两个或多个时间在同一时间段内发生(交替执行)
并行:指两个或多个事件同一时间内发生(同时发生)
2.进程与线程
进程:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。(资源、独立)
进程至少含一个线程(每个进程都有一个主线程),进程能创建、撤销线程
线程:线程是进程的一个实体,是CPU调度和分配的基本单位。(运算)
线程能创建线程,但不能创建进程。(也就是说线程是进程的一部分)
Thread类
创建线程1
class PrimeThread extends Thread {
long minPrime;
PrimeThread() {
}
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
//获取线程的名称
String name = getName();
Thread t = Thread.currentThread()
String name = t.getName()
//设置线程的名称
设置线程的名称:(了解)
1.使用setName(String name);
2.创建一个带参数的构造方法,参数传递线程的名称,调用父类的带参构造方法,把线 程名称传递给父类,让父类给子线程起名字
3.public static void sleep(long millis):以指定的毫秒数暂停(暂时停止执 行)
毫秒数结束后,线程继续执行
. . .
}
//然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143);
p.start();
创建线程2
实现步骤:
1.创建一个Runnable接口的实现类
2.在实现类中重写run方法
3.创建一个Runnable接口的实现类对象
4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5.调用Thread类中的start方法,开启新的线程,执行run方法
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
//然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
实现和继承方式创建线程的好处
1.一个类只能继承一个类,一个类继承了Thread类就不能继承其他类
实现Runnable接口创建多线程程序的好处,避免了单继承的局限性
2.增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行分离(解耦)
实现类中,重写了run方法:用来设置线程任务
创建Thread类的对象,调用start方法,用来开启新的线程
public static Thread currentThread():返回对当前正在执行的线程对象的引用(类类型的变量)
匿名内部类方式实现线程的创建
public class Demo01InnerClassThread {
public static void main(String[] args) {
//线程父类为Thread
new Thread() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"java");
}
}
}.start();
//线程的接口
Runnable r = new Runnable(){//接口等于实现类,是多态
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"python");
}
}
};
new Thread(r).start();
//还可以
new Thread(new Runnable(){//接口等于实现类,是多态
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"python");
}
}
}).start();
}
}
线程安全解决方案
同步代码块
解决线程安全问题的一种方案:使用同步代码块
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一一个
3.锁对象作用:
把同步代码块锁住,只让一一个线程在同步代码块中执行
同步方法
使用步骤;
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码}
然后run方法里面调用这个方法就行了
Lock锁机制(更先进)
Loke接口的方法:
void lock() //获取锁
void unloce() //释放锁
java.util.concurrent.locks.ReentantLoce implements Lock接口
使用步骤:
1.在成员位置创建一一个ReentrantLockL(Loke接口的实现类)对象
2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
3.在可能会出现安全问题的代码后调用Loc k接口中的方法unloc k释放锁
线程状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uy9e7bPR-1597819106502)(G:\图片素材\java\1596359544293.png)]
1.初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2.运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3.阻塞(BLOCKED):表示线程阻塞于锁。
4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6.终止(TERMINATED):表示该线程已经执行完毕。
等待唤醒机制
经典案列
线程池
线程池:JDK1.5之后提供的
java. util. concurrent . Executors :线程池的工厂类,用来生成线程池
Executors类中的静态方法: .
static ExecutorService newFixedThreadPool(int nThreads) 创建一个 可重用固定线程数的线程池
参数:
int nThreads :创建线程池中包含的线程数量
返回的是Executorservice接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
java. util. concurrent . ExecutorService :线程池接口
用来从线程池中获职线程,调用start方法。执行线程任务
submit(Runnable task) 提交-个Runnable 任务用于执行
关闭/销毁线程池的方法
void shutdown()
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newThreadPools生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit传递线程任务(实现类),开启线程执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池(不建议这样做)
n()
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newThreadPools生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit传递线程任务(实现类),开启线程执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池(不建议这样做)