多线程
Thread
通过创建类继承Thread类,实现多线程
package com.java;
public class Demo01 {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for(int i=0;i<10;i++){
System.out.println("汗滴禾下土"+i);
}
}
static class Mythread extends Thread {
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
//这里的代码 就是一条新的执行路径
//这个执行路径的触发方式,不是调用run方法,而是通过Thread对象的start方法
for(int i = 0;i<10;i++){
System.out.println("锄禾日当午"+i);
}
}
}
}
/*
汗滴禾下土0
锄禾日当午0
汗滴禾下土1
汗滴禾下土2
汗滴禾下土3
锄禾日当午1
汗滴禾下土4
锄禾日当午2
汗滴禾下土5
锄禾日当午3
汗滴禾下土6
锄禾日当午4
汗滴禾下土7
锄禾日当午5
汗滴禾下土8
锄禾日当午6
汗滴禾下土9
锄禾日当午7
锄禾日当午8
锄禾日当午9
*/
Runnable
Runnable属于接口类
实现Runnable 与 继承Thread相比有如下优势:
1. 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
2. 可以避免单继承所带来的局限性
3. 任务与线程本身是分离的,提高了程序的健壮性
4. 后续学习的线程池技术,接受Runnable类型的任务,不接收Thread类型的线程
package com.java;
public class Demo02 {
/**
* 多线程技术
* 实现Runnable 与 继承Thread相比有如下优势:
* 1. 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
* 2. 可以避免单继承所带来的局限性
* 3. 任务与线程本身是分离的,提高了程序的健壮性
* 4. 后续学习的线程池技术,接受Runnable类型的任务,不接收Thread类型的线程
* @param args
*/
public static void main(String[] args) {
//实现Runnable
MyRunnable r = new MyRunnable();
//1. 创建一个任务对象
//为线程定义名称
Thread t = new Thread(r,"线程1");
//2。 创建一个线程,并为其分配一个任务
t.start();
//获取子线程名称
System.out.println(t.getName());
//获取当前主线程的名称
System.out.println(Thread.currentThread().getName());
}
static class MyRunnable implements Runnable{
@Override
public void run() {
//线程的任务
for(int i=0;i<10;i++){
System.out.println("床前明月光"+i);
}
}
}
}
/*
线程1
main
床前明月光0
床前明月光1
床前明月光2
床前明月光3
床前明月光4
床前明月光5
床前明月光6
床前明月光7
床前明月光8
床前明月光9
*/
线程中断
代码实现如下:
package com.java;
public class Demo05 {
public static void main(String[] args) {
//线程中断
//一个线程是一个独立的执行结构,他是否应该结束,应该由其自身决定
MyRunnable m = new MyRunnable();
Thread t = new Thread(m);
t.start();
for(int i=0;i<5;i++){
//获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//给线程添加中断“标记”,当发现标记时,run方法就会抛出异常,进入catch块
t.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//发现标记后进入到这里,线程还不会立即中断,必须使用1return方法
System.out.println("发现了标记,利用return自杀");
//通过使用return结束run方法,来结束该线程
return;
}
}
}
}
}
线程休眠
通过使用Thread类下的sleep方法可以实现线程休眠
package com.java;
public class Demo03 {
/**
* 多线程技术
* @param args
*/
public static void main(String[] args) throws InterruptedException {
//线程休眠技术 sleep
for(int i=0;i<10;i++){
Thread.sleep(2000);
System.out.println(i);
}
}
}
/*
Thread-0:0
main:0
Thread-0:1
main:1
main:2
Thread-0:2
Thread-0:3
main:3
Thread-0:4
main:4
Thread-0:5
发现了标记,利用return自杀
*/
守护线程
线程:分为守护线程和用户线程
用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
守护线程:守护用户线程,当最后一个线程结束时,所有守护线程自动死亡
代码实现案例:
package com.java;
public class Demo06 {
public static void main(String[] args) {
//线程:分为守护线程和用户线程
//用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
//守护线程:守护用户线程,当最后一个线程结束时,所有守护线程自动死亡
Thread t = new Thread(new MyRunnable());
//设置t为守护线程
t.setDaemon(true);
t.start();
for(int i=0;i<5;i++){
//获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//主线程结束,守护线程t结束
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现了标记,利用return自杀");
//通过使用return结束run方法,来结束该线程
return;
}
}
}
}
}
//运行结果,主线程结束时,守护线程自动结束
/*
main:0
Thread-0:0
Thread-0:1
main:1
Thread-0:2
main:2
Thread-0:3
main:3
Thread-0:4
main:4
Thread-0:5
*/
线程不安全
代码实现案例:
package com.java;
public class Demo07 {
/**
* 线程不安全问题
* @param args
*/
public static void main(String[] args) {
Ticket run = new Ticket();
//同时使用三个线程去执行
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count=4;
@Override
public void run() {
while (count > 0) {
System.out.println("正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:" + count);
}
}
}
}
//运行结果,票数中存在负票数
/*
正在卖票
正在卖票
正在卖票
出票成功,余票:3
正在卖票
出票成功,余票:3
正在卖票
出票成功,余票:3
正在卖票
出票成功,余票:2
正在卖票
出票成功,余票:1
正在卖票
出票成功,余票:2
正在卖票
出票成功,余票:0
出票成功,余票:-1
出票成功,余票:0
*/
1. 同步代码块
同步代码块(锁机制),三个线程不能同时执行该代码块
package com.java;
public class Demo08 {
/**
* 线程不安全问题
* 解决方案1:同步代码块
* @param args
*/
public static void main(String[] args) {
Ticket run = new Ticket();
//同时使用三个线程去执行
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count=10;
private Object o = new Object();
@Override
public void run() {
//同步代码块(锁机制),三个线程不能同时执行该代码块
while (true) {
//任何的对象都可以成为锁,可以理解为都可以打上锁的标记
synchronized (o) {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:" + count);
} else {
break;
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//运行结果
/*
Thread-0正在卖票
出票成功,余票:9
Thread-2正在卖票
出票成功,余票:8
Thread-1正在卖票
出票成功,余票:7
Thread-2正在卖票
出票成功,余票:6
Thread-0正在卖票
出票成功,余票:5
Thread-2正在卖票
出票成功,余票:4
Thread-1正在卖票
出票成功,余票:3
Thread-2正在卖票
出票成功,余票:2
Thread-0正在卖票
出票成功,余票:1
Thread-2正在卖票
出票成功,余票:0
*/
2. 同步方法
package com.java;
public class Demo09 {
/**
* 线程不安全问题
* 解决方案2:同步方法
* @param args
*/
public static void main(String[] args) {
Ticket run = new Ticket();
//同时使用三个线程去执行
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count=10;
private Object o = new Object();
@Override
public void run() {
//同步代码块(锁机制),三个线程不能同时执行该代码块
while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
//被synchronized修饰的方法为同步方法,线程需排队执行
public synchronized boolean sale() {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:" + count);
return true;
}
return false;
}
}
}
3. 显示锁
package com.java;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo10 {
/**
* 同步代码块 和 同步方法 都属于隐式锁
* 解决方案3:显示锁 lock 子类 ReentrantLock
* @param args
*/
public static void main(String[] args) {
Ticket run = new Ticket();
//同时使用三个线程去执行
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count=10;
//显示锁 l : fair参数为true 就是公平锁(谁先来谁先得到锁)
private Lock l = new ReentrantLock(true);
@Override
public void run() {
//同步代码块(锁机制),三个线程不能同时执行该代码块
while (true) {
//上锁
l.lock();
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:" + count);
}else {
break;
}
//解锁
l.unlock();
}
}
}
}
线程池 Executor
如果并发的线程数量很多,并且每个线程都是执行一个时间短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间,线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
Java中的四种线程池
1. 缓存线程池
长度的无限制
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在则创建线程,并放入线程池,然后使用
- 存在则使用
package com.java;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo11 {
/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
//让主线程休眠 1 秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//此时上面有三个空闲的线程,随机调用一个进行使用
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
}
}
//运行结果
/*
线程的名称:pool-1-thread-1
线程的名称:pool-1-thread-2
线程的名称:pool-1-thread-3
线程的名称:pool-1-thread-1
*/
2. 定长线程池
长度是指定的数值
任务加入后的执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
- 不存在空闲线程,且线程已满的情况下,则等待线程池存在的空闲线程
- 存在则使用
package com.java;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo12 {
/**
* 定长线程池.
* (长度是指定的数值)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//已经在线程池中创建了两个线程,该线程需要等待3s后才会出现空闲线程,然后使用
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
}
}
/*
线程的名称:pool-1-thread-1
线程的名称:pool-1-thread-2
线程的名称:pool-1-thread-1
*/
3. 单线程线程池
执行流程:
- 判断线程池的 哪个线程 是否空闲
- 空闲则使用
- 不空闲,则等待池中的单个线程空闲后使用
- 空闲则使用
package com.java;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo13 {
public static void main(String[] args) {
/**
* 单线程线程池.
* 执行流程:
* 1. 判断线程池 的那个线程 是否空闲
* 2. 空闲则使用
* 4. 不空闲,则等待 池中的单个线程空闲后 使用
*/
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
}
}
/*
线程的名称:pool-1-thread-1
线程的名称:pool-1-thread-1
*/
4. 周期定长线程池
周期任务 定长线程池、
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程未满的情况下,则创建线程并放入线程池,然后使用
- 不存在空闲线程,且线程已满的情况下,则等待线程池存在空闲线程
周期性任务执行时:
定期执行,当某个实际出发时,自动执行某任务
package com.java;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo14 {
public static void main(String[] args) {
/**
* 周期任务 定长线程池.
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*
* 周期性任务执行时:
* 定时执行, 当某个时机触发时, 自动执行某任务 .
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定时执行
* 参数1. runnable类型的任务
* 参数2. 时长数字
* 参数3. 时长数字的单位
*/
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑");
}
},5, TimeUnit.SECONDS);
/**
* 周期执行
* 参数1. runnable类型的任务
* 参数2. 时长数字(延迟执行的时长)
* 参数3. 周期时长(每次执行的间隔时间)
* 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("嘿嘿嘿");
}
},5,2,TimeUnit.SECONDS);
}
}
/*
嘿嘿嘿
俩人相视一笑
嘿嘿嘿
嘿嘿嘿
*/