###多线程###
1. 基本概念
进程:
程序(任务)的执行过程 ---> 动态性(必须是可执行文件执行后)
持有资源(共享内存,共享文件)和线程 --->载体
线程:
源代码文本编辑
源代码编译
语法校验
... ...
线程是系统中最小的执行单眼,同一个进程中有多个线程,线程共享进程的资源。
线程的交互:互斥、同步
2. 线程中的常用方法
1)Java对线程的支持
Thread常用方法:
线程的创建:
Thread()
Thread(String name)
Thread(Runnable target)
Thread(Runnable target,String name)
线程的方法:
void start() --->启动线程
static void sleep(long millis) --->线程休眠
static void sleep(long millis , int nanos) --->线程休眠
void join() --->
void join(long millis) ---> 使其他线程等待当前线程终止
void join(long millis , int nanos) --->
static void yield() --->当前运行线程释放处理器资源
获取线程的引用:
static Thread currentThread() --->返回当前运行的线程引用
例:需求设置一个舞台剧
1. 主角设定
单线程运行:
package homework;
import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;
import sun.applet.Main;
public class Actor extends Thread{
public void run(){
System.out.println(getName()+"我是一个演员");
int count = 0;
boolean keepRunning = true;
while (keepRunning) {
if(count == 10){
keepRunning = false;
}
if(count%5 == 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "登台演出" + (++count));
}
System.out.println(getName()+"的演出结束了!");
}
}
public static void main(String[] args) {
Thread actor = new Actor();
actor.setName("Mr Thread");
actor.start();
}
双线程运行:
static class Actress implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"我是一个演员");
int count = 0;
boolean keepRunning = true;
while (keepRunning) {
if(count == 10){
keepRunning = false;
}
if(count%5 == 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "登台演出" + (++count));
}
System.out.println(Thread.currentThread().getName()+"的演出结束了!");
}
}
public static void main(String[] args) {
Thread actor = new Actor();
actor.setName("Mr Thread");
actor.start();
new Thread(new Actress(),"Ms.Runnable").start();
}
2. 群演
package homework;
//大舞台
public class Stage extends Thread {
public void run() {
QunYan qunYan1 = new QunYan();
QunYan qunYan2 = new QunYan();
//创建Runnable接口
Thread qunY1 = new Thread(qunYan1, "路人卖糖葫芦的");
Thread qunY2 = new Thread(qunYan2, "路人甲");
//启动线程,让群演上场
qunY1.start();
qunY2.start();
//舞台线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
qunYan1.keepRuning = false;
qunYan2.keepRuning = false;
try {
qunY1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Stage().start();
}
3. 主角登场
package homework;
public class Zhujue extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName()+"该上场了");
for(int i =0; i<5;i++){
System.out.println(Thread.currentThread().getName()+"开始了他的表演");
}
System.out.println(Thread.currentThread().getName()+"退场了");
}
}
package homework;
//大舞台
public class Stage extends Thread {
public void run() {
System.out.println("欢迎观看热巴大舞台");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
QunYan qunYan1 = new QunYan();
QunYan qunYan2 = new QunYan();
//创建Runnable接口
Thread qunY1 = new Thread(qunYan1, "路人卖糖葫芦的");
Thread qunY2 = new Thread(qunYan2, "路人甲");
//启动线程,让群演上场
qunY1.start();
qunY2.start();
//舞台线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正当路人都没有什么表演时,主角上场了");
Thread zj = new Zhujue();
zj.setName("迪丽热巴");
System.out.println("热巴上场,观众欢呼!");
//停止线程方法
qunYan1.keepRuning = false;
qunYan2.keepRuning = false;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
zj.start();
try {
zj.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("迪丽热巴登场增加了舞台的精彩,完美了舞台剧");
}
}
public static void main(String[] args) {
new Stage().start();
}
2. 如何正确的停止Java中的线程
1)stop()方法:禁用,会使线程立刻停止,无法得知线程进行到哪一步,也无法完成清理工作
---------------------------------------------------------错误的方法
2)如何停止线程:使用退出的标志
比如:我们利用boolean类型结束循环去停止线程,这样可以完整的执行完线程后停止,也可以有充足的时间去清理
3. 线程间的交互
1)争用条件:当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现场称为争用条件。
2)互斥与同步:
synchronized ()
同步实现:wait() / notify() / notifyAll()
例:
package homework;
public class ThreadTest {
static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
System.out.println("thread-0线程执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread-0其它代码....");
}
}).start();
new Thread(() -> {
synchronized (obj) {
System.out.println("thread-1线程执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread-1其它代码....");
}
}).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒obj上其它线程");
synchronized (obj) {
obj.notifyAll();
}
}
}
4. 线程状态
NEW(新建) 线程刚被创建,但是还没有调用 start方法
RUNNABLE(可运行) 当调用了start() 方法之后
BLOCKED(阻塞) 当线程进入了monitor监视器区,处于entrySet里准备竞争锁的时候,处于阻塞状态
WAITING(等待) 当调用了对象的wait方法,或调用了线程对象的join方法,进入了WaitSet,处于等待状态
TIMED_WAITING 当调用wait(long n) join(long n) 进入了WaitSet,处于有限时的等待状态
当调用sleep(long n) 是让当前线程放弃cpu的时间片,睡眠一会
TERMINATED (终止)当线程代码运行结束
5. 线程池
创建有限的线程资源为更多的任务提供服务
一个核心的ExecutorService的实现类:ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
corePoolSize 核心线程数目 (最多保留的线程数)
maximumPoolSize
workQueue 阻塞队列 如果任务超过了核心线程数,进入队列进行排队,直到有空闲的线程
如果任务过多,阻塞队列都放不下了,还会创建新的线程来救急
corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)会抛出拒绝提交任务异常
keepAliveTime 生存时间- 针对救急线程
unit 时间单位 秒
创建固定大小的线程池:
Executors.newFixedThreadPool(2);
核心线程数=最大线程数(没有救急线程被创建)
阻塞队列 无界,可以放任意数量的任务,
适合执行数量有限,长时间运行的任务
创建缓冲线程池
Executors.newCachedThreadPool()
核心线程数是0, 最大线程数是Integer的最大值(救急线程可以无限创建)
生存时间是60s
适合任务数比较密集,但每个任务执行时间较短的情况
创建单线程线程池
Executors.newSingleThreadExecutor()
使用场景:希望多个任务排队执行
区别:
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改
经典例题:生产者消费者
package homework;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class Product{
public Product(int i) {
}
}
public class ThreadTest2 {
private static BlockingQueue<Product> queue = new ArrayBlockingQueue<>(5);
public static void main(String[] args) {
// 生产者线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
Product p = new Product(i);
System.out.println(Thread.currentThread().getName()+"生产了:"+p);
try {
queue.put(p);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 消费者线程
for (int j = 0; j < 5; j++) {
new Thread(()->{
for (int i = 0; i < 2; i++) {
try {
Product p = queue.take();
System.out.println(Thread.currentThread().getName()+"消费了:"+p);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
####END####