线程
java并发编程,多线程编程,能实现线程中多个任务同时并发的执行
程序:指令和数据的集合,编写代码
进程:将编写的程序,计算机指令集合启动(应用程序eg:工厂)
线程:一个进程中可以包含多个线程任务,cpu对线程的执行是在线程之间进行切换执行。线程为进程的逻辑单位(线程:工厂中每条流水线的工人)
线程执行需要获得CPU设备,os系统会在多个线程之间进行CPU的轮流切换,由os进行调度
一个程序启动形成进程后,一个进程可以存在多个执行路径
1.线程的实现:方式一
package Thread;
/**
* Thread类的对象表示JVM中的线程
* 自定义线程类需要继承Thread类,自定义类对象才能变为线程
*/
public class DemoTest01 extends Thread{
//重写run方法,该方法中封装线程任务,每个线程需要完成的事
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
//获得当前线程对像
System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
}
}
public static void main(String[] args) {
//创建3个线程,jvm为每个线程起一个默认的名称
DemoTest01 d1 = new DemoTest01();
DemoTest01 d2 = new DemoTest01();
DemoTest01 d3 = new DemoTest01();
/*启动线程,实现线程让线程变为就绪状态,排队等待os分配cpu
* 当cpu分配给某个线程后,jvm会自动调用run方法完成线程任务*/
d1.start();
d2.start();
d3.start();
}
}
2.线程的实现:方式二
package Thread;
public class ThreadDemoTest02 implements Runnable {
/*创建Runnable接口,重写run方法封装线程任务
* 该类的对象只是封装了线程的任务,并不是线程对象*/
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "移动" + i + "步");
}
}
public static void main(String[] args) {
//创建封装线程任务的对象
ThreadDemoTest02 y = new ThreadDemoTest02();
ThreadDemoTest02 m = new ThreadDemoTest02();
ThreadDemoTest02 s = new ThreadDemoTest02();
/*创建Thread对象,分配线程任务,线程对象和线程任务是分离的
* 同时指定线程名字,线程名字默认是Thread--数字*/
Thread t1 = new Thread(y, "join");
Thread t2 = new Thread(m, "lisa");
Thread t3 = new Thread(s, "tom");
t1.start();
t2.start();
t3.start();
}
}
3.线程池
池化技术:在高并发,小任务的环境,如果存在对象的频繁创建和销毁,增加了系统堆内存的io负担,影响执行效率。--需要使用池化技术。
池化技术:将需要频繁创建和销毁的对象,创建好后放入一个池容器中,用的时候从容器中获得该对象,用完之后不需要是释放该对象,继续归还到池容器中。
池化技术的指标属性
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:线程池的核心线程数。即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。
maximumPoolSize:最大线程数。超过此数量,会触发拒绝策略 如果对象不够使用的时候,可以创建的对象的最大值
keepAliveTime:线程的存活时间。当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则线程退出。(线程在不处理任务的情况下,处于空闲状态,而且存活线程的数量大于核心数量,会销毁掉大于核心数量的线程)
unit:指定keepAliveTime的单位。比如:秒:TimeUnit.SECONDS。
workQueue:一个阻塞队列,提交的任务将会被放到这个队列里。(如果当前需要处理的任务大于线程对象最大数量,需要将不能及时处理的线程任务进行排队保存,保存到队列中。)
threadFactory:线程工厂,用来创建线程,主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。
handler:拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用。
默认拒绝策略为AbortPolicy。即:不执行此任务,而且抛出一个运行时异常
4.线程池的实现一
Exceutors接口:提供绑定线程任务的方法,所有线程池都实现该接口
ExecutorService接口:提供关闭线程池,获得某个线程执行后的Future结果
Executors工具类:创建线程池的工具类,提供静态方法更具需要创建不同线程池
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemoTest03 {
public static void main(String[] args) {
//创建线程池对象,存在3个线程
ExecutorService service = Executors.newFixedThreadPool(3);
//给线程池对象中线程绑定线程任务
service.execute(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
}
}
});
//该任务的处理时前三个任务那个线程处理完,由哪个线程处理
service.execute(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
}
}
});
}
}
#创建线程池对象存在3个线程
#给线程池对象中每一个绑定线程任务,如果只创建3个线程对象,而绑定4个线程任务,则最先执行完成的处理第四个线程任务
pool-1-thread-1射击第1颗子弹。。。
pool-1-thread-2射击第1颗子弹。。。
pool-1-thread-3射击第1颗子弹。。。
pool-1-thread-2射击第2颗子弹。。。
pool-1-thread-1射击第2颗子弹。。。
pool-1-thread-2射击第3颗子弹。。。
pool-1-thread-3射击第2颗子弹。。。
pool-1-thread-2射击第4颗子弹。。。
pool-1-thread-1射击第3颗子弹。。。
pool-1-thread-2射击第5颗子弹。。。
pool-1-thread-3射击第3颗子弹。。。
pool-1-thread-2射击第6颗子弹。。。
pool-1-thread-1射击第4颗子弹。。。
pool-1-thread-2射击第7颗子弹。。。
pool-1-thread-3射击第4颗子弹。。。
pool-1-thread-2射击第8颗子弹。。。
pool-1-thread-1射击第5颗子弹。。。
pool-1-thread-2射击第9颗子弹。。。
pool-1-thread-3射击第5颗子弹。。。
pool-1-thread-2射击第10颗子弹。。。(2是最先执行完成的)
pool-1-thread-1射击第6颗子弹。。。
pool-1-thread-3射击第6颗子弹。。。
pool-1-thread-1射击第7颗子弹。。。
pool-1-thread-3射击第7颗子弹。。。
pool-1-thread-1射击第8颗子弹。。。
pool-1-thread-3射击第8颗子弹。。。
pool-1-thread-1射击第9颗子弹。。。
pool-1-thread-3射击第9颗子弹。。。
pool-1-thread-1射击第10颗子弹。。。
pool-1-thread-3射击第10颗子弹。。。
pool-1-thread-2射击第1颗子弹。。。
pool-1-thread-2射击第2颗子弹。。。
pool-1-thread-2射击第3颗子弹。。。
pool-1-thread-2射击第4颗子弹。。。
pool-1-thread-2射击第5颗子弹。。。
pool-1-thread-2射击第6颗子弹。。。
pool-1-thread-2射击第7颗子弹。。。
pool-1-thread-2射击第8颗子弹。。。
pool-1-thread-2射击第9颗子弹。。。
pool-1-thread-2射击第10颗子弹。。。
5.线程池的实现方式二
package Thread;
import java.util.concurrent.*;
public class ThreadDemoTest04 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(3);
//线程池绑定线程任务
service.submit(new Runnable() {
@Override
public void run() {
//重写run封装线程任务
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() +
"射击第" + i + "颗子弹、、、");
}
}
});
/*submit 第一个参数封装线程任务
* 第二个参数封装线程任务执行后的返回值*/
Future<Integer> future = service.submit(new Runnable() {
@Override
public void run() {
//重写run封装线程任务
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() +
"射击第" + i + "颗子弹、、、");
}
}
}, 8);
Integer integer = future.get();
System.out.println("integer" + integer);
//绑定线程池任务
Future<Integer> submit = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//重写run封装线程任务
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() +
"射击第" + i + "颗子弹、、、");
}
return 7;
}
});
Integer integer1 = future.get();
System.out.println("integer1" + integer1);
}
}
pool-1-thread-1射击第1颗子弹、、、
pool-1-thread-2射击第1颗子弹、、、
pool-1-thread-1射击第2颗子弹、、、
pool-1-thread-2射击第2颗子弹、、、
pool-1-thread-1射击第3颗子弹、、、
pool-1-thread-2射击第3颗子弹、、、
pool-1-thread-1射击第4颗子弹、、、
pool-1-thread-2射击第4颗子弹、、、
pool-1-thread-1射击第5颗子弹、、、
pool-1-thread-2射击第5颗子弹、、、
pool-1-thread-1射击第6颗子弹、、、
pool-1-thread-2射击第6颗子弹、、、
pool-1-thread-1射击第7颗子弹、、、
pool-1-thread-2射击第7颗子弹、、、
pool-1-thread-1射击第8颗子弹、、、
pool-1-thread-2射击第8颗子弹、、、
pool-1-thread-1射击第9颗子弹、、、
pool-1-thread-2射击第9颗子弹、、、
pool-1-thread-1射击第10颗子弹、、、
pool-1-thread-2射击第10颗子弹、、、
integer8
integer18
pool-1-thread-3射击第1颗子弹、、、
pool-1-thread-3射击第2颗子弹、、、
pool-1-thread-3射击第3颗子弹、、、
pool-1-thread-3射击第4颗子弹、、、
pool-1-thread-3射击第5颗子弹、、、
pool-1-thread-3射击第6颗子弹、、、
pool-1-thread-3射击第7颗子弹、、、
pool-1-thread-3射击第8颗子弹、、、
pool-1-thread-3射击第9颗子弹、、、
pool-1-thread-3射击第10颗子弹、、、
Process finished with exit code -1
6.线程池的实现三
线程池实现定时任务和延迟任务:
延迟任务:在项目启动多长时间以后,执行某段代码
定时任务:在项目启动后,可以指定什么时间开始执行,指定间隔多长时间继续执行(频率)
package Thread;
import org.w3c.dom.ls.LSOutput;
import java.util.Date;
import java.util.concurrent.*;
public class ThreadPoolDemo04 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*定义任务--定时任务程序中某个代码在某个时刻启动,每隔多长时间执行一次
* 延时任务--系统启动后延迟多少时间才执行某段代码*/
//创建线程池对象
ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
//给线程池线程绑定线程任务
service.schedule(new Runnable() {
//封装线程任务
@Override
public void run() {
System.out.println("---" + new Date());
}
}, 2, TimeUnit.SECONDS);
//主线程main方法中打印
System.out.println("main:" + new Date());
//定时任务
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--" + new Date());
}
}, 0, 1, TimeUnit.SECONDS);
//主线程main方法打印
System.out.println("main:" + new Date());
//定时任务
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--"+new Date());
}
},0,1,TimeUnit.SECONDS);
//给线程绑定线程任务,获得线程任务后结果
ScheduledFuture<Integer> future = service.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "--"+ new Date());
return 100;
}
}, 5, TimeUnit.SECONDS);
System.out.println("future="+future.get());
//关闭线程池
service.shutdown();
}
}
main:Wed Dec 06 17:13:25 CST 2023
main:Wed Dec 06 17:13:25 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:25 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:25 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:26 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:26 CST 2023
---Wed Dec 06 17:13:27 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:27 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:27 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:28 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:28 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:29 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:29 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:30 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:30 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:30 CST 2023
future=100
Process finished with exit code 0
7.线程的生命周期
新建状态:当使用new关键字创建线程Thread类的对象,当前对象为线程的新建状态。
就绪状态:当通过某个线程对象调用了start方法,该线程处于就绪状态,等到os分配CPU。
在没有得到CPU的情况下,进行排队,处于就绪队列中。
执行状态:就绪状态的线程在获得CPU的情况下,开始处理线程任务 ,执行run方法。
死亡状态:当执行状态的线程,处理完线程任务后,自动销毁。内存没有该线程对象。
阻塞状态:当前正在执行的线程,失去CPU资源(被动,主动,OS调度),处于阻塞状态
阻塞状态的线程可以直接获得CPU变为执行状态,也可以变为就绪状态
8.java线程通信
sleep();
join();
yield();
setDeamon();
setPriority();
9.线程安全问题
介绍:当多个线程访问共享资源(同一资源)的时候出现程序执行结果和预期结果不一致的情况--线程安全。
共享资源(临界资源)。
控制对共享资源按照业务规则,实现对共享资源访问的原子性,怎么实现共享资源访问的原子性--需要对访问共享资源的代码加锁。
所实现了计算机线程指令执行的原子性,保证了数据的安全。
会将程序执行的效率,并发性。是一种排他说,悲观锁。同时只能有一个线程获得锁。
10.synchronized实现代码枷锁
使用synchronized修饰方法
package Thread;
public class Ticket implements Runnable {
/*实现售票,是每个窗口的任务,线程任务
* 定义同步代码块
* synchronized (JAVA对象)*/
private static int num = 100;
private static Object obj = new Object();
@Override
public void run() {
for (int i = 0; i <=100 ; i++) {
synchronized (obj){//同步代码快
if (num>0){//判断是否有余票
System.out.println(Thread.currentThread().getName()+
"卖出的票号:"+num+"的票");
num--;
}
}
}
}
public static void main(String[] args) {
//创建三个线程对象,表示三个窗口。封装线程任务
Ticket t = new Ticket();
for (int i = 0; i <=30 ; i++) {
Thread t1 = new Thread(t);
//启动线程
t1.start();
}
}
}
定义同步代码块
package Thread;
public class Ticket implements Runnable {
/*实现售票,是每个窗口的任务,线程任务
* 定义同步代码块
* synchronized (JAVA对象)*/
private static int num = 100;
private static Object obj = new Object();
@Override
public void run() {
for (int i = 0; i <=100 ; i++) {
synchronized (obj){//同步代码快
if (num>0){//判断是否有余票
System.out.println(Thread.currentThread().getName()+
"卖出的票号:"+num+"的票");
num--;
}
}
}
}
public static void main(String[] args) {
//创建三个线程对象,表示三个窗口。封装线程任务
Ticket t = new Ticket();
for (int i = 0; i <=30 ; i++) {
Thread t1 = new Thread(t);
//启动线程
t1.start();
}
}
}
11.lock锁的使用
12. 生产者和消费模型
生产者和消费者模型--主要是先多个线程执行不同任务的同步。
比如:公交车司机和售票员--公交车司机没进站,停车。售票员不能开门。
公交车司机和售票员--售票员没有关门,公交车司机不能开出和出栈。
Producer:不能实现数据的重复生产,如果容器Buffer已经存在数据,Producer不能生产,直到Consumer消费完成数据。
Consumer:已经消费了,不能实现数据重复消费,消费者不能继续消费,直到Producer生产数据。
创建容器类
package Thread;
/**
* 容器类
*/
public class Buffer {
private int num = 0;//容器中保存的数据
//true 有 false 表示没有
private boolean flag = false;//表示容器中是否已经生成数据
/*定义方法给容器中存放数据*/
public synchronized void put(int num){
try {
if(flag){//已经存在保存的数据,不能生产
//当前线程阻塞,生产者等待,直到其他线程调用notifyAll方法
//this.wait();
}
//开始生产
this.num = num;
this.notifyAll();//唤醒消费者
//将flag赋值为
flag= true;
}catch (Exception ex){
ex.printStackTrace();
}
}
//定义方法获得容器中的数据
public synchronized int pop(){
try {
if(!flag){
//消费者等待,直到其他线程调用notifyAll方法
this.wait();
}
}catch (InterruptedException e) {
e.printStackTrace();
}
//开始消费
flag =false;
this.notifyAll();//唤醒生产者
return this.num;
}
}
声明容器属性
package Thread;
public class Producer implements Runnable{
//声明容器属性对象
private Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
//生产者实现生产任务
buffer.put(i+100);
System.out.println(Thread.currentThread().getName()+
"生产数据是:"+(i+100));
//sleep阻塞
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
创建Conusmer类
package Thread;
public class Conusmer implements Runnable {
//声明容器属性对象
private Buffer buffer;
public Conusmer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
//生产者实现生产任务
int pop = buffer.pop();
System.out.println(Thread.currentThread().getName()+
"消费数据"+pop);
//sleep阻塞
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
}
}
创建ProducerConsumerTet类
package Thread;
public class ProducerConsumerTet {
public static void main(String[] args) {
//创建容器对象
Buffer buffer = new Buffer();
//创建封装生产者任务的线程
Producer producer = new Producer(buffer);
//创建消费者任务的线程
Conusmer conusmer = new Conusmer(buffer);
/*创建生产者和消费者线程*/
Thread t1 = new Thread(producer);
Thread t2 = new Thread(conusmer);
t1.start();
t2.start();
}
}
13.线程总结
Java多线程编程,线程的实现方式
继承Thread类,该类子类的对象就是一个线程对象。
重写run方法,重写run方法指定线程任务
实现Runnable接口,重写run方法封装线程任务
创建Thread对象,传入Runnable接口实现,指定线程任务
线程池实现,创建多个Thread对象保存到一个集合中,
给线程池中某个线程池对象绑定线程任务,处理完成线程任务后,该线程对象不会销毁,
归还到线程池。建立程序在高并发,小任务环境中线程对象的频繁创建和销毁,减少对
内存的IO操作,提高程序执行的效率。
Executor接口:提供给线程池中线程绑定任务的方法
|--ExecutorService接口:
绑定线程任务
Runnable接口绑定任务,方法没有返回值
Callable接口绑定任务,方法存在返回值,可以获得任务执行后的返回值关闭线程池
Executors工具类:提供静态方法,创建线程池
线程池核心指标:
线程的核心数量
线程的最大数量
线程存活时间(当线程的最大数量>线程的核心数量,如果在一段时间内,线程没有参
数任务的处理,可以将线程对象销毁)。
Java线程通信的方法
新建状态
就绪状态
运行状态
消亡状态
阻塞状态
sleep(2000);
join();
yield();
setPriority()
setDeamon()
Java线程安全问题
当多个线程池并发访问共享资源,出现运行结果和预期结果不一致---出现线程安全问题。
解决线程安全问题--给访问共享资源的代码加锁,利用Java对象的锁。
实现代码执行的原子性---在计算机中某些代码语句不能拆分执行,必须看作一个整体执
行。使用synchronized加锁
synchronized 修饰方法,定义同步方法,利用的是调用该方法的this对象的锁,如果是static
方法,利用的是Class对象的锁。
synchronized修饰代码块,定义同步代码块,需要指定对象。
线程实现方式:
继承Thread
1 需要创建多个线程,就创建多个Thread类的子类对象,如果需要实现多个线程共享
资源,就必须将子类的属性修饰为静态的
2 Java的继承是单根继承,所以为了合理利用继承资源,不建议使用继承Thread实现
实现Runable接口
1 实现线程任务和线程的分离,创建实现类的对象,只是创建了封装线程任务的对象。
2 创建Thread对象,指定线程任务,传入封装线程任务的对象
3 可以实现多个线程对资源的共享。
线程池
线程安全:
线程加锁,利用Java对象锁
加锁的目的是为了实现操作的原子性,锁是排他锁(悲观锁)。
synchronized 隐式锁,可以修饰同步方法,可以修饰同步代码块
lock 显式锁,需要集合try-finally,手动加锁和释放锁。