多线程基础
1、线程简介
任务:进程,线程,多线程
现实中很多的例子,其实本质上大脑CPU统一时间依旧只做了一件事情!
-
线程就是独立的执行路径;
-
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如cpu调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
Process与Thread
-
程序:静态的一组指令和数据的集合
-
进程:执行程序的一次执行过程,是动态的;
进程是系统资源分配的单位
; -
线程:线程就是独立执行的路径,一个进程中包含1-n个线程;
线程是CPU调度和执行的单位
2、线程实现(重点)
任务:Thread、Runnable、Callable
三种创建方式
1、基础Thread 类
2、实现Runnable 接口
3、实现Callable 接口
Thread
start方法:线程交替执行
run方法:按顺序执行
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 1:53
* 创建线程方式1
*/
//1、继承Thread类
public class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
//2、重写run 方法
@Override
public void run() {
for (int i = 1; i <= 2000; i++) {
System.out.println("线程循环:" + i);
}
}
public static void main(String[] args) {
//3、start 启动运行
PrimeThread p = new PrimeThread(143);
p.start();
for (int i = 1; i <= 2000; i++) {
System.out.println("主线程循环:" + i);
}
}
}
Runnable
推荐使用:同一个对象可以被多次调用,避免OOP单继承的局限性
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 1:57
* 创建线程方式2
*/
//1、实现Runnable接口
public class PrimeRunable implements Runnable {
long minPrime;
PrimeRunable(long minPrime) {
this.minPrime = minPrime;
}
//2、重写run方法
@Override
public void run() {
System.out.println("启动线程" + minPrime);
}
public static void main(String[] args) {
//3、创建线程对象,调用start()方法启动线程
PrimeRunable p = new PrimeRunable(143);
new Thread(p, "线1").start();
new Thread(p, "线2").start();
new Thread(p, "线3").start();
}
}
案例:分析多线程下载图片的顺序
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 12:28
* 练习Thread实现图片 多线程下载
*/
public class PictureThread extends Thread {
private String url;//图片地址
private String name;//保存的图片名
public PictureThread(String url, String name) {
this.url = url;
this.name = name;
}
//下载图片的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为:" + name);
}
public static void main(String[] args) {
PictureThread p1 = new PictureThread("https://cdn2.jianshu.io/assets/default_avatar/2-9636b13945b9ccf345bc98d0d81074eb.jpg","1.jpg");
PictureThread p2 = new PictureThread("http://seopic.699pic.com/photo/50163/5313.jpg_wh1200.jpg","2.jpg");
PictureThread p3 = new PictureThread("https://highpic.originoo.com/highpic/detail/161025/RM/bji81201635.jpg", "3.jpg");
p1.start();
p2.start();
p3.start();
}
}
//下载器
class WebDownloader {
//下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
结果每次都不一样,并不是我们想的1,2,3的顺序
案例:龟兔赛跑-Race
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 19:57
* 模拟龟兔赛跑
*/
public class Race implements Runnable {
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休眠
if (Thread.currentThread().getName().equals("兔子") && i % 50 == 0) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//如果比赛结束了就停止程序
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步!");
}
}
//判断是否完成比赛
private boolean gameOver(int steps) {
if (winner != null) {//已经存在胜利者了
return true;
}
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "乌龟").start();
new Thread(race, "兔子").start();
}
}
乌龟取得了胜利,兔子走到50步的时候睡觉去了!
案例:初始并发问题
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 19:46
* 多线程购票 初始并发问题
* 发现问题:多个线程操作同一个对象时,线程不安全,数据紊乱。
*/
public class BuyTickets implements Runnable {
//票数
private int tickeNums = 10;
@Override
public void run() {
while (true) {
if (tickeNums <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickeNums-- + "票");
}
}
public static void main(String[] args) {
BuyTickets b = new BuyTickets();
new Thread(b, "小周").start();
new Thread(b, "肖鑫").start();
new Thread(b, "黄牛").start();
new Thread(b, "小明").start();
new Thread(b, "肖红").start();
new Thread(b, "萧兰").start();
}
}
并发操作同一个对象
Callable了解即可
1. 实现 Callable 接口,需要返回值类型
2. 重写 call 方法,需要抛出异常
3. 创建目标对象
4. 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
5. 提交执行: Future result1 = ser.submit(t1);
6. 获取结果: boolean r1 = result1.get()
7. 关闭服务: ser.shutdownNow();
演示:利用callable改造下载图片案例
有返回值
可以抛出异常
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 12:28
* 创建线程方式3 改造下载图片案例
*/
//1、实现Callable接口,需要返回值类
public class PrimeCallable implements Callable<Boolean> {
private String url;//图片地址
private String name;//保存的图片名
public PrimeCallable(String url, String name) {
this.url = url;
this.name = name;
}
//2、重写call方法,需要抛出异常
@Override
public Boolean call() throws Exception {
Boolean bool;
WebDownloader2 webDownloader = new WebDownloader2();
bool = webDownloader.downloader(url, name);
System.out.println("下载了文件名为:" + name);
return bool;
}
public static void main(String[] args) throws Exception {
//3、创建目标对象
PrimeCallable p1 = new PrimeCallable("https://cdn2.jianshu.io/assets/default_avatar/2-9636b13945b9ccf345bc98d0d81074eb.jpg", "1.jpg");
PrimeCallable p2 = new PrimeCallable("http://seopic.699pic.com/photo/50163/5313.jpg_wh1200.jpg", "2.jpg");
PrimeCallable p3 = new PrimeCallable("https://highpic.originoo.com/highpic/detail/161025/RM/bji81201635.jpg", "3.jpg");
//4、创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//5、提交执行
Future<Boolean> result1 = ser.submit(p1);
Future<Boolean> result2 = ser.submit(p2);
Future<Boolean> result3 = ser.submit(p3);
//6、获取结果
Boolean r1 = result1.get();
Boolean r2 = result2.get();
Boolean r3 = result3.get();
//7、关闭服务
ser.shutdownNow();
System.out.println("r1:" + r1 + "\tr2:" + r2 + "\tr3:" + r3);
}
}
//下载器
class WebDownloader2 {
//下载方法
public boolean downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
return false;
}
return true;
}
}
结果:如何只创建一个池子 Executors.newFixedThreadPool(1); 结果就是1-2-3!
3、线程状态
五大状态:
1、对象一旦创建就进入到了新建状态
2、当调用start()方法,线程立即进入就绪状态
,但不意味着立即调度执行
3、cpu调度后进入运行状态
,线程才真正执行线程体的代码块
4、当调用sleep,wait 或同步锁定时,线程进入阻塞状态
,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu调度执行。
5、线程中断或者结束,一旦进入死亡状态
,就不能再次启动
线程方法
方 法 | 说 明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,别用这个方式 |
boolean isAlive() | 测试线程是否处于活动状态 |
终止线程
建议线程正常停止
建议使用标志位
不建议使用过期的JDK方法stop/destory等
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 22:20
* 停止线程
*/
public class StopThread implements Runnable {
//1、定义一个标志位
boolean flag = true;
int count;
@Override
public void run() {
while (flag) {
System.out.println("跑步,run跑了-->" + count++ + "步");
}
}
//2、设置公开的停止方法
public void stop() {
flag = false;
}
public static void main(String[] args) {
StopThread p = new StopThread();
new Thread(p).start();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
int timeno = 0;
@Override
public void run() {
timeno++;
System.err.println("timeno:"+timeno);
if(timeno==10){
p.stop();
timer.cancel();
System.out.println("线程停止,关闭定时器");
}
}
},0,100);
}
}
线程休眠 sleep
sleep不会是否锁,每个对象都有一个锁
sleep后的单位是ms,存在异常InterruptedException
休眠后进入就绪状态,可以用来模拟网络延时,倒计时
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 22:54
* 使用线程模拟倒计时
*/
public class CountDownSheep {
public static void main(String[] args) throws InterruptedException {
tenDown();
}
private static void tenDown() throws InterruptedException {
//获取系统当前时间
Date now = new Date(System.currentTimeMillis());
while (true) {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(now));
now = new Date(System.currentTimeMillis());
}
}
}
结果每秒输出一次
线程礼让 yield
让当前执行的线程暂停,但不阻塞;线程由运行状态转为就绪状态,等待CPU调度
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 23:08
* 线程礼让,礼让不一定成功,看CPU心情
*/
public class YieldThread {
public static void main(String[] args) {
Mythread p = new Mythread();
new Thread(p,"A").start();
new Thread(p,"B").start();
}
}
class Mythread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--》线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"--》线程停止执行");
}
}
礼让成功和礼让不成功的结果
线程插队 join
线程插队,其他线程阻塞,让插队线程先执行
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 23:16
* 线程插队
*/
public class JsonThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程VIP来了-->" + i);
}
}
public static void main(String[] args) throws InterruptedException {
JsonThread p = new JsonThread();
Thread thread = new Thread(p);
thread.start();
//主线程
for (int i = 0; i < 500; i++) {
if (i == 200) {
thread.join();//插队
}
System.out.println("main==》" + i);
}
}
}
查看JDK帮助文档
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 23:25
* 观察线程状态
*/
public class StateThread {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(".....");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//NEW
//观察启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//RUNNABLE
while (state != Thread.State.TERMINATED) {//TERMINATED 已退出的线程处于此状态
Thread.sleep(100);
state = thread.getState();//更新线程状态
System.out.println(state);//输出转态
}
}
}
线程优先级 priority
Java提供了一个线程调度器 来监控就绪状态的线程,按照优先级1-10来决定调度那个线程先执行
优先级高的不一定先运行,只是概率高
MIN_PRIORITY = 1;
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;
/**
* @author 流浪不是我的初衷
* @create 2020-06-27 23:42
* 线程优先级
*/
public class PriorityThread {
public static void main(String[] args) {
//默认优先级
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
MyPriority p = new MyPriority();
Thread t1 = new Thread(p,"t1");
Thread t2 = new Thread(p,"t2");
Thread t3 = new Thread(p,"t3");
Thread t4 = new Thread(p,"t4");
Thread t5 = new Thread(p,"t5");
Thread t6 = new Thread(p,"t6");
//先设置优先级再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//10
t4.start();
t5.setPriority(8);
t5.start();
t6.setPriority(11);
t6.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
//打印线程名--》优先级
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
守护线程 daemon
线程分为用户线程和守护线程
daemon = false; 默认是false 用户线程
虚拟机必须确保用户线程执行完毕 eg:main()
虚拟机不用等待守护线程执行完毕 eg:gc()
/**
* @author 流浪不是我的初衷
* @create 2020-06-28 0:00
* 测试守护线程
*/
public class DaemonThread {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false 用户线程,这里设置为true 守护线程
thread.start();//上帝守护线程启动
new Thread(you).start();//你 用户线程启动
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝保佑着你");
}
}
}
//你 人生不过3w天
class You implements Runnable {
@Override
public void run() {
//100年
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.err.println("============goodbye world!");//Hello World!
}
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章