多线程的学习
多线程的学习(java.Thread)
线程的简介
任务、进程、线程、多线程
多任务
如:边吃饭边玩手机,就是多任务,但其实本质上我们的大脑还是只进行一个任务,上一秒吃饭,下一秒玩手机,因为转换太快了所以忽略了。
多线程
如:一条道路分有多条道,汽车并排骑行也不会有问题,这就是多线程。
如:五个人同时开黑打游戏,游戏实现了多线程。
普通方法调用和多线程
1、普通方法执行效率低
2、多线程执行效率高
程序进程和线程
系统中我们运行的程序就是进程,如:QQ、微信和B站这些就是进程,如:播放视频同时有图像、声音、弹幕这些是同时执行线程的结果,也就是多线程。
进程(Process)、线程(Thread)
程序:是指令和数据的有序集合,没有运行的含义,是一个静态概念。
而进程是执行程序的过程,是一个动态概念,是系统资源分配的单位。
通常一个进程中可以包含多个线程(同时有图像、声音、弹幕),一个进程至少有一个线程,线程是cpu调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指由多个cpu,即多核。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换很快,所以就有同时执行的错觉
核心概念
1、线程是独立执行的路径
2、程序运行时,即使没有自己创建线程,后台也会有多个线程,如:主线程、gc线程。
3、main()称之为主线程,为系统的入口,用户执行整个程序。
4、进程中有多个线程,线程的运行由调度器安排调度,调度器与操作系统紧密相关,先后顺序不能人为干预。
5、对同一资源操作时,存在资源抢夺问题,需要并发控制。
6、线程会带来额外的开销,如:cpu调度时间,并发控制开销。
7、每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
线程创建
class Thread
继承Object类
实现Runnable接口类
线程是程序执行中的线程,java虚拟机允许程序同时执行多个执行线程
创建步骤:
1、自定义线程类继承Tread类。
2、重写run()方法,编写线程执行体。
3、创建线程对象,调用start()方法启动线程。
public class StartThread extends Thread{
// 线程入口点
@Override
public void main(){
...
}
}
public static void main(String[] args) {
// 创建线程对象
StartThread t = new StrartThread();
t.start();
}
网图下载
1、配置环境
1.1下载jar包:Apache commons-io
2、将jar添加到程序中
2.1 删除导入的commons-io jar包
2.2 添加jar包到项目中
3、代码
通过继承线程实现,图片下载
package com.zhou.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread1 extends Thread{
private String url; // 网络图片路径
private String name; // 文件名称
// 构造方法
public TestThread1(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) {
TestThread1 t1 = new TestThread1("http://bpic.51yuansu.com/activity/20200408/5e8d446443d63.jpg","1.jpg");
TestThread1 t2 = new TestThread1("http://bpic.51yuansu.com/activity/20200326/5e7c19d39bf8d.jpg","2.jpg");
TestThread1 t3 = new TestThread1("http://bpic.51yuansu.com/activity/20191025/5db2c3d021879.jpg","3.jpg");
// 开启
t1.start();
t2.start();
t3.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方法出现问题!");
}
}
}
}
实现Runnable接口
package com.zhou.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread2 implements Runnable{
private String url; // 网络图片路径
private String name; // 文件名称
// 构造方法
public TestThread2(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) {
TestThread2 t1 = new TestThread2("http://bpic.51yuansu.com/activity/20200408/5e8d446443d63.jpg","1.jpg");
TestThread2 t2 = new TestThread2("http://bpic.51yuansu.com/activity/20200326/5e7c19d39bf8d.jpg","2.jpg");
TestThread2 t3 = new TestThread2("http://bpic.51yuansu.com/activity/20191025/5db2c3d021879.jpg","3.jpg");
// 创建线程对象通过线程,通过线程对象来开启我们的线程,代理
// Thread thread = new Thread(t1);
// thread.start();
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).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方法出现问题!");
}
}
}
}
4、小结
4.1 继承Thread类
子类继承Thread类具备多项成能力
启动线程:子类对象.start()
不建议使用:避免oop单继承单继承局限性
4.2 实现Runnable接口
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免但继承局限性,灵活方便,方便同一个对象呗多个线程使用
线程并发
1、多个线程操作一个对象
多个线程操作同一个资源的情况,线程不安全,数据混乱
package com.zhou.demo01;
// 多个线程操作同一个资源的情况先,线程不安全,数据混乱
public class TestThread4 implements Runnable {
private int ticketNums = 10;
@Override
public void run() {
while(true) {
if (ticketNums <= 0) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"小明").start();
new Thread(ticket,"小红").start();
new Thread(ticket,"小王").start();
}
}
龟兔赛跑
package com.zhou.demo01;
public class TestThread5 implements Runnable{
private static String winner;
@Override
public void run() {
for(int i = 0;i <= 100;i++) {
// 模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")) {
try {
Thread.sleep(20);
} 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;
}else {
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
TestThread5 race = new TestThread5();
new Thread(race,"乌龟").start();
new Thread(race,"兔子").start();
}
}
实现callable
静态代理
如:结婚,婚庆公司帮你处理结婚的事(现场布置安排),结婚:实现结婚接口即可。
静态代理总结:
1、真是角色和代理都要实现同一个接口
2、代理中有真是角色
好处
1、代理可以做真是角色做不了的事情
2、真实角色只需要专注做自己的事情
package com.zhou.demo2;
// 静态代理总结
// 真是角色和代理同时实现同一个接口
// 代理中要有真是角色
// 好处
// 代理可以做真是角色做不了的事情
// 真是角色只需要专注做自己的事情
public class Stacticproxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappanMarry();
}
}
// 结婚接口
interface Marry{
void HappanMarry();
}
class You implements Marry{
@Override
public void HappanMarry() {
System.out.println("老哥结婚!");
}
}
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappanMarry() {
before();
this.target.HappanMarry();
after();
}
private void after() {
System.out.println("结婚后,收尾款!");
}
private void before() {
System.out.println("结婚前,安排结婚事项!");
}
}
lamda表达式
为什么要用lamda表达式
简洁代码
lamda表达式代码
package com.zhou.Demo03;
public class TestLamda1 {
// 2、内部静态类
static class Like2 implements Ilike{
@Override
public void lamda() {
System.out.println("I like lamda 2!");
}
}
public static void main(String[] args) {
// 1、执行外部类
// 接口new一个实现类(like),则该接口的实现类都能够使用,如:2、执行内部.. (可以用)相当于
// 如果是实现类new一个实现类,则变量只能在使用在这个实现类
Ilike like = new Like();
like.lamda();
// 2、执行内部静态类
Like2 like2 = new Like2();
like2.lamda();
// 相当于:
// like = new Like2();
// like.lamda();
// 3、局部类
class Like3 implements Ilike{
@Override
public void lamda() {
System.out.println("I like lamda 3!");
}
}
// 3、执行内部类
Like3 like3 = new Like3();
like3.lamda();
// 4、匿名类
Like like4 = new Like() {
@Override
public void lamda() {
System.out.println("I like lamda 4!");
}
};
// 4、执行匿名类
like4.lamda();
// 5、用lamda简化
Ilike like5 = () -> {
System.out.println("I like lamda 5!");
};
// 5、执行lamda简化
like5.lamda();
}
}
interface Ilike {
void lamda();
}
// 1、外部实体类
class Like implements Ilike{
@Override
public void lamda() {
System.out.println("I like lamda!");
}
}
线程停止
代码
package com.zhou.Demo04;
public class TestStop implements Runnable{
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run..Thread--" + i++);
}
}
// 设公开停止线程的方法
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop t = new TestStop();
new Thread(t).start();
for (int i =0;i < 1000;i++) {
System.out.println("main.." + i);
if (i == 900) {
t.stop();
System.out.println("线程停止!");
}
}
}
}
slepp休眠
购票
package com.zhou.Demo05;
public class TestSleep implements Runnable {
private int ticketNums = 10;
@Override
public void run() {
while (true) {
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticketNums <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了" + ticketNums-- + "张票。");
}
}
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
new Thread(testSleep,"小明").start();
new Thread(testSleep,"小红").start();
new Thread(testSleep,"小王").start();
}
}
倒计时
package com.zhou.Demo05;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep1 {
public static void tenDown() {
int num = 10;
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num--);
if (num == 0) {
break;
}
}
}
public static void main(String[] args) {
// 倒数计时
// tenDown();
// 系统时间
Date startTime = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程礼让
假设有A、B线程,A、B同时运行,A得到cpu的调度,这时A礼让重新出来出来与B竞争,这时候谁能得到调度还是看cpu心情。
// 线程礼让
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"吃饭").start();
new Thread(myYield,"睡觉").start();
}
}
class MyYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始!");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "线程停止!");
}
}
join(线程优先运行权)
线程中的vip,此线程执行完成后在执行其他线程。
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0;i < 100;i++) {
System.out.println("线程中的vip" + i);
}
}
public static void main(String[] args) {
TestJoin testjoin = new TestJoin();
Thread thread = new Thread(testjoin);
thread.start();
// 主线程
for (int i =0;i < 300;i++) {
if (i == 200) {
try {
thread.join(); // 线程vip先执行完这个,线程后,才会执行下一个线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main" + i);
}
}
}
state(线程状态)
new之后是就绪状态,调度进入运行状态,之后进入阻塞状态或是结束状态(结束后就不能再次启动)。
public class TestState {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0;i < 5;i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("/////");
});
// 观察状态
Thread.State state = thread.getState();
System.out.println("观察状态:" + state);
// 观察启动后
thread.start();
state = thread.getState();
System.out.println("启动后:" + state);
while (state != Thread.State.TERMINATED) { // 只要线程不终止,就一直是输出状态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState(); // 更新线程状态
System.out.println(state); // 输出状态
}
//thread.start(); // 结束后是不能再运行的
}
}
优先级(不影响cpu的调度,越高资源越多)
public class Priority {
public static void main(String[] args) {
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
t1.start(); // 默认线程优先级为:5
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(7);
t4.start();
t5.setPriority(9);
t5.start();
t6.setPriority(Thread.MAX_PRIORITY); // 表示最高优先级即10
t6.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程的优先级:" + Thread.currentThread().getPriority());
}
}
守护(daemon)线程:程序结束还会继续运行,虚拟机停止才会停止
- 线程分为 用户线程和 守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕,虚拟机运行结束是守护线程也结束
- 守护线程有:后台记录操作日志,监控内存,垃圾回收等待...
public class Deamon {
public static void main(String[] args) {
You you = new You();
God god = new God();
// 启动上帝线程
Thread thread = new Thread(god);
// 守护线程是程序结束后还会继续执行,虚拟机停止守护线程才会结束
thread.setDaemon(true); //设置为true,是守护线程。默认为false(即用户线程)
thread.start(); // 启动上帝守护线程,守护一生
new Thread(you).start(); // 你启动线程
}
}
// 上帝守护你(守护线程:垃圾回收等)
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("上帝守护!");
}
}
}
// 你
class You implements Runnable {
@Override
public void run() {
// 人的一生差不多是100年
for (int i = 0; i < 365000; i++) {
System.out.println("你开心的一生!");
}
System.out.println("结束goodbye!");
}
}
同步方法()
并发:同一对象被多个线程同时操作
队列:如人们吃饭排队一样,知识程序中年叫队列
等待池:如100个人进行排队的地址
队列锁:如电影中以前排队进入公共电话亭打电话,每一个进去的人都会锁住们不让他人进来。
锁机制(synckronized):安全性提高,但拖慢线程。
public class Ticket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"苦逼的我").start();
new Thread(buyTicket,"牛逼的你们").start();
new Thread(buyTicket,"可恶的黄牛党").start();
}
}
// 购票人
class BuyTicket implements Runnable {
private int ticket = 10;
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// synchronized 同步方法 锁的是对象本身(购票人:BuyTicket)
private synchronized void buy() throws InterruptedException {
if (ticket <= 0) {
flag = false;
return ;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到第" + ticket-- + "票");
}
}
死锁
如:两个小孩,一个小孩有一个玩具车、另一个小孩有玩具枪,都各自想要对方的玩具,自己手中的玩具也一直拿在自己手中,这时候就形成了死锁。
程序也一样,一个线程拥有了两个以上的死锁。
// 死锁 多线程互相把持对方所需资源,又不会换取,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑娘");
Makeup g2 = new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
// 口红
class Lipstick {
}
// 镜子
class Mirror {
}
// 化妆
class Makeup extends Thread {
// 需要的资源只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 选择
String girlName; // 使用化妆物品的人
// 构造器
Makeup(int choice,String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆 互相只有对方所需资源
private void makeup() throws InterruptedException {
// 解开死锁
// 互相持有对方所需资源,同时放开自己的资源,使用对方持有的资源
if (choice == 0) {
synchronized (lipstick) { // 获得口红锁
System.out.println(this.girlName + ":获取口红");
Thread.sleep(1000);
}
synchronized (mirror) { // 获得镜子锁
System.out.println(this.girlName + ":获取镜子");
}
} else {
synchronized (mirror) { // 获得镜子锁
System.out.println(this.girlName + ":获取镜子");
Thread.sleep(1000);
}
synchronized (lipstick) { // 获得口红锁
System.out.println(this.girlName + ":获取口红");
}
}
/* // 互相持有对方所需资源,自己的资源拿在手中但又都想获得对方资源,
if (choice == 0) {
synchronized (lipstick) { // 获得口红锁
System.out.println(this.girlName + ":获取口红");
Thread.sleep(1000);
synchronized (mirror) { // 获得镜子锁
System.out.println(this.girlName + ":获取镜子");
}
}
} else {
synchronized (mirror) { // 获得镜子锁
System.out.println(this.girlName + ":获取镜子");
Thread.sleep(1000);
synchronized (lipstick) { // 获得口红锁
System.out.println(this.girlName + ":获取口红");
}
}
}*/
}
}
锁
public class LockTicket {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;
// 定义lock锁,可重用锁ReetrantLock
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock(); //加锁,是程序进行排队,一个一个进行,知道结束。如果不加锁会出现负数
try {
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
} else {
break;
}
} finally {
lock.unlock(); // 解锁
}
}
}
}
线程协作
生产者消费模式:如你去肯德基,你(消费者)到前台告诉服务员要一份童子鸡,前台通知厨师(生产者),厨师开始制作鸡完成后,给前台,前台给你。
管程法
// 测试:生产者消费者模型 -> 利用缓冲区解决:管程法
// 生产者 消费者 缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
// 生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
// 生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
container.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产了" + i + "只鸡");
}
}
}
// 消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了" + container.pop().id + "只鸡!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 产品
class Chicken{
int id; // 产品编号
public Chicken(int id) {
this.id = id;
}
}
// 缓冲区
class SynContainer{
// 需要一个容器大小
Chicken[] chickents = new Chicken[10];
// 容器计数器
int count = 0;
// 生产者放入产品
public synchronized void push(Chicken chicken) throws InterruptedException {
// 如果容器满了,就需要等待消费
if (count == chickents.length) {
// 通知消费者消费,生产等待
this.wait();
}
// 如果没有满,我们就需要丢入产品
chickents[count] = chicken;
count++;
// 可以通知消费者消费了
this.notify();
}
// 消费者消费产品
public synchronized Chicken pop() throws InterruptedException {
// 判断能否消费
if (count == 0) {
// 等待生产者生产,消费者等待
this.wait();
}
// 如果可以消费
count--;
Chicken chicken = chickents[count];
// 吃完了,通知生产者生产
this.notify();
return chicken;
}
}
线程池
// 测试线程池
public class TestPool {
public static void main(String[] args) {
// 1.创建服务,创建线程池
// newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2.关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
学习观看的视频为:【狂神说Java】多线程详解