文章目录
多线程详解
多任务:多个事情一起做,多个线程同时执行
多线程:一个程序中多个函数同时进行
程序、进程、线程:程序代码是静态的,程序执行开启一个进程,其中可以运行多个线程
线程
- 线程的独立的执行路径
- 在程序运行时,即使没有自己创建的线程,后台也会有很多个线程,如主线程,gc线程
- main函数作为主线程,是程序的入口,用于执行整个程序
- 在一个进程中国,如果开辟了多个线程,线程的执行是由调度器安排调度的,调度器是与操作系统有着紧密联系的先后顺序不是人为干预的
- 对于操作同一份资源时,会存在资源抢夺的问题,需要加入并发控制
- 多线程会带来额外的开销,如CPU调度时间,并发控制时间
- 每个线程都在自己的工作内存交互,内存控制不当会造成数据的不一致
Thread类
-
继承Thread类,重写run方法
//继承一个线程类 public class TestTread01 extends Thread { @Override public void run() { for (int i = 0; i < 20 ; i++) { System.out.println("我在看代码"+i); } } public static void main(String[] args) { new TestTread01().start(); for (int i = 0; i < 2000; i++) { System.out.println("我在学习多线程"+i); } }
-
继承runnable接口
//继承一个runnable接口 public class TestTread01 implements Runnable { @Override public void run() { for (int i = 0; i < 20 ; i++) { System.out.println("我在看代码"+i); } } public static void main(String[] args) { TestTread01 tread01 = new TestTread01(); new Thread(tread01).start(); for (int i = 0; i < 2000; i++) { System.out.println("我在学习多线程"+i); } } }
-
线程中简单的并发问题
//继承一个runnable接口,多个线程使用同一个数据,买票的并发问题 public class TestTread01 implements Runnable { int tick = 10; @Override public void run() { while (true) { synchronized (this) { //解决多进程同时访问问题 if (tick < 0) { return; } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "拿到了第" + tick + "票"); tick--; } } } public static void main(String[] args) { TestTread01 tread01 = new TestTread01(); new Thread(tread01 , "小明").start(); new Thread(tread01 , "老师").start(); new Thread(tread01 , "黄牛们").start(); } }
-
龟兔赛跑问题中的并发
public class Race implements Runnable{ private String winner; @Override public void run() { for (int i = 0; i <= 100 ; i++) { if(Thread.currentThread().getName().equals("兔子") && i%10==0){ try { Thread.sleep(1); //让兔子线程休息 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); boolean flag = win(i); //有胜利者跳出循环 if (flag){ break; } } } public boolean win(int i){ //判断是否有胜利者 if (winner!=null){ return true; }{ if (i >= 100){ winner = Thread.currentThread().getName(); System.out.println("winner is " + winner); } } return false; } public static void main(String[] args) { Race race = new Race(); new Thread(race , "兔子").start(); new Thread(race , "乌龟").start(); } }
-
Callable接口
使用自制下载器common-io的支持,实现Callable接口,实现下载图片并返回结果。
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.*; public class CallableD implements Callable<Boolean> { private String url; private String name; //下载文件的构造器 public CallableD(String url, String name) { this.url = url; this.name = name; } //实现的call方法 @Override public Boolean call() throws Exception { WebDownload webDownload = new WebDownload(); webDownload.downloadImage(url ,name); System.out.println("下载了文件"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { CallableD c1 = new CallableD("https://i0.hdslb.com/bfs/sycp/creative_img/92d5c884711f98fab0362c15e08cd885", "1.jpg"); CallableD c2 = new CallableD("https://i0.hdslb.com/bfs/sycp/creative_img/92d5c884711f98fab0362c15e08cd885", "2.jpg"); CallableD c3 = new CallableD("https://i0.hdslb.com/bfs/sycp/creative_img/92d5c884711f98fab0362c15e08cd885", "3.jpg"); ExecutorService ser = Executors.newFixedThreadPool(3); //创建一个线程池,n=3三个线程的池 Future<Boolean> re1 = ser.submit(c1); //线程提交执行并返回结果 Future<Boolean> re2 = ser.submit(c2); Future<Boolean> re3 = ser.submit(c3); Boolean a1 = re1.get(); //得到线程的结果 Boolean a2 = re2.get(); Boolean a3 = re3.get(); ser.shutdownNow(); //关闭服务线程 } } //自制下载器 class WebDownload { public void downloadImage(String url , String name){ try { FileUtils.copyURLToFile(new URL(url) , new File(name)); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
静态代理模式
- 结婚
- 婚庆公司帮助你其他的事情(代理)
- 结婚完成
//代理模式总结:
//代理和真识角色都要实现接口
//代理要代理一个真实对象
//代理对象可以做很多附加的事情
//真是对象将做专注的事情
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}
interface Marry{
//人间四大喜事
//久旱逢甘露
//他乡遇故知
//洞房花烛夜
//金榜题名时
void HappyMarry();
}
class You implements Marry{ //实际需求对象
@Override
public void HappyMarry() {
System.out.println("快乐结婚");
}
}
class WeddingCompany implements Marry{
private You you;
public WeddingCompany(You you) { //代理构造器,他是谁的代理,哪一个类的代理
this.you = you;
}
@Override
public void HappyMarry() {
before(); //代理所添加的服务
you.HappyMarry(); //代理主要做的事
after(); //代理所添加的服务
}
private void before() {
System.out.println("布置现场");
}
private void after() {
System.out.println("收尾款");
}
}
代理和Thread的联系
//Thread代理一个线程类进行执行
new Thread( ()->System.out.println("我是真实对象") ).start();
//WeddingCompany代理一个You对象执行操作
new WeddingCompany(new You()).HappyMarry();
Lambda表达式
- 使用:匿名内部类过多,代码看起来很简洁,去掉一些没有意义的代码,只留下核心逻辑
- 习惯就好
- 关键是理解函数式接口
- 任何接口,如果只有唯一一个抽象方法,那么他就是函数式接口
- 对于函数式接口,我们可以通过Lambda表达式来创建该接口对象
public class Lamda {
public static void main(String[] args) {
Like like =new Like(){
@Override
public void like() {
System.out.println("我喜欢你4");
}
};
Like like1 = new ILike();
Like like2 = new ILike2();
//局部内部类
class ILike3 implements Like{
@Override
public void like() {
System.out.println("我喜欢你3");
}
}
Like like3 = new ILike3();
like.like();
like1.like();
like2.like();
like3.like();
//lamda表达式
Like like4 = ()->{
System.out.println("我喜欢你4");
};
like4.like();
}
//静态内部类
static class ILike2 implements Like{
@Override
public void like() {
System.out.println("我喜欢你2");
}
}
}
//外部类
class ILike implements Like {
@Override
public void like() {
System.out.println("我喜欢你");
}
}
//函数式接口,提供Lamda使用的
interface Like {
public void like();
}
//有参数和无参数等简化形式的Lambda表达式
Like like5 = ()-> System.out.println("我喜欢你5");
like5.like();
like4.like();
Life life = a -> System.out.println("生活有多喜欢你" + a);
life.life(5);
Life life1 = (a) -> System.out.println("生活有多喜欢你" + a);
life1.life(6);
线程的五大状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZVessZ9-1637032108060)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211102085114284.png)]
- 新生状态:Thread t = new Thread();线程对象创建成功后就进入了新生状态。
- 就绪状态:当线程调用start()方法,线程立即进入就绪状态,等待CPU的调度执行
- 运行状态:当CPU调度完成后,线程进入CPU执行,线程真正开始执行代码。
- 阻塞状态:当执行中的线程调用sleep方法,线程将进入阻塞状态,停止线程的运行,当阻塞时间解除时,线程进入就绪状态,重新等待CPU的调度
- 死亡状态:当线程中断或者结束,线程记入死亡状态,线程将不会再次启动,直到被销毁
停止线程
- JDK不建议使用stop方法强制停止线程
- 使用flag标志位,外部干预使线程自己停止
public class TestClose extends Thread{
private boolean flag = true;
@Override
public void run() {
while (flag){
System.out.println("线程正在执行");
}
}
public void stoped(){
this.flag = false;
}
public static void main(String[] args) {
TestClose testClose = new TestClose();
testClose.start();
for (int i = 0; i < 10 ; i++) {
try {
sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main在监控");
if (i == 9){
testClose.stoped();
}
}
}
}
线程休眠
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在异常InterruptedException
-
sleep时间达到后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
每个对象都有一个锁,sleep不会释放锁
try { sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!看CPU心情
public class TestYeild {
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()方法,线程插队
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("VIP线程来了");
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 900; i++) {
if (i == 500){
thread.join();
}
System.out.println("main在监控");
}
}
}
线程状态观测
- 线程状态Thread.state
- NEW:尚未启动的线程,处于new状态
- RUNABLE:在java虚拟机中执行的线程处于runable状态
- BLOCKED:被阻塞等待监视器锁定的线程处于blocked状态
- WAITING:正在等待另一个线程执行特定动作的线程处于waiting状态
- TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于timed_waiting状态
- TERMINAED:已退出的线程处于terminaed状态
- 一个线程可以在给定时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("/");
}
});
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
while( state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级
- java提供一个线程调度器来监控程序中启动后进入就绪状态的所有进程。线程调度其按照优先级决定应该调度哪个线程来执行
- 线程优先级用数字来表示。范围1~10
- Thread.MIN_PRIORITY = 1
- Thread.MAX_PRIORITY = 10
- Thread.NORM_PRIORITY = 5
- 使用getPriority(),setPriority(int n)来改变和获取优先级
public class TestPriority implements Runnable{
int i = 0;
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---->" +(i++) );
}
public static void main(String[] args) {
TestPriority testPriority = new TestPriority();
Thread thread01 = new Thread(testPriority , "thread01");
Thread thread02 = new Thread(testPriority , "thread02");
Thread thread03 = new Thread(testPriority , "thread03");
Thread thread04 = new Thread(testPriority , "thread04");
Thread thread05 = new Thread(testPriority , "thread05");
thread01.setPriority(1);
thread01.start();
thread02.setPriority(2);
thread02.start();
thread03.setPriority(Thread.NORM_PRIORITY);
thread03.start();
thread04.setPriority(8);
thread04.start();
thread05.setPriority(Thread.MAX_PRIORITY);
thread05.start();
}
}
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如后台记入操作日志,监控内存,垃圾回收等待
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
//设置为守护线程,默认为用户线程
thread.setDaemon(true);
//启动线程
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() {
System.out.println("hell word");
for (int i = 0; i < 100; i++) {
System.out.println("活着一定要开心呀");
}
System.out.println("Goodbye");
}
}
线程同步机制
- 处理多线程问题时,每个线程访问同一个对象,切某些线程还行修改这个对象。这时候我们就应该需要同步机制,线程同步实质就是一种等待机制,多个访问的线程进入这个对象等待池形成队列,等待前面的线程使用结束下一个线程访问
- 形成条件:队列(queue)和锁(synchronized)
- 一个线程持有锁对到子其他线程挂起
- 在多线程竞争的情况下,加锁和释放锁会导致比较多的上下文切换和调度延迟的问题,引起性能的问题
- 如果一的优先级高的线程在等待一个优先级低的线程释放锁则会引起优先级倒置的问题
三种不安全多线程机制
//线程中买票的不安全机制
public class UnSafeTicket {
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 {
int ticket = 10;
boolean flag = true;
@Override
public void run() {
while(flag){
buy();
}
}
private void buy() {
if (ticket <= 0){
flag = false;
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---》买到了第:" +ticket--+"票");
}
}
//不安全的银行账户
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
UnSafeBanks me = new UnSafeBanks(account, 50, "我");
UnSafeBanks wife = new UnSafeBanks(account, 100, "媳妇");
me.start();
wife.start();
}
}
class Account{
int money;
String name;
public Account(int money,String name) {
this.money = money;
this.name = name;
}
}
class UnSafeBanks extends Thread {
private Account account;
private int drawingMoney;
private int nowMoney;
public UnSafeBanks(Account account, int drawingMoney , String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
if (account.money - drawingMoney <0){
System.out.println("钱不够了");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" +account.money);
System.out.println(this.getName() + "手里有余额:" + nowMoney);
}
}
//不安全的arrayList数组
public class UnSafeList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
同步方法
- 我们通过过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制。这套机制就是synchronized关键字,他包括两种用法:synchronized方法和synchronized块
- synchronized方法控制对象的访问,每个对象对应一把锁每个synchronized方法都必须获得调用该方法的锁才能执行,否则线程会阻塞。方法一执行,就会独占该锁,直到方法结束才会释放锁,后面被阻塞的线程才能得到这个锁,继续执行
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
UnSafeBanks me = new UnSafeBanks(account, 50, "我");
UnSafeBanks wife = new UnSafeBanks(account, 100, "媳妇");
me.start();
wife.start();
}
}
class Account{
int money;
String name;
public Account(int money,String name) {
this.money = money;
this.name = name;
}
}
class UnSafeBanks extends Thread {
private Account account;
private int drawingMoney;
private int nowMoney;
public UnSafeBanks(Account account, int drawingMoney , String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//锁住变化的量,锁住了对象
synchronized (account) {
if (account.money - drawingMoney < 0) {
System.out.println("钱不够了");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里有余额:" + nowMoney);
}
}
}
死锁
-
两个线程相互持有互相需要的资源,造成双方都无法正常执行等待。
public class DeadLock { public static void main(String[] args) { MakeFace makeFace = new MakeFace( 0 ,"可爱"); MakeFace makeFace1 = new MakeFace( 1 ,"善良"); makeFace.start(); makeFace1.start(); } } class Lipstick { } class Mirror { } class MakeFace extends Thread{ static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int chose; public MakeFace(int chose ,String name) { super(name); this.chose = chose; } @Override public void run() { makeup(); } public void makeup(){ if(chose == 0){ synchronized (lipstick){ System.out.println(this.getName() + "在使用镜子"); synchronized (mirror){ System.out.println(this.getName() + "在使用口红"); } } }else { synchronized (mirror){ System.out.println(this.getName() + "在使用口红"); synchronized (lipstick){ System.out.println(this.getName() + "在使用镜子"); } } } } }
Lock锁
- Lock是显式锁(手动开关闭锁,别忘记关锁)synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展型
- 使用顺序:Lock >> 同步代码块 >> 同步方法
- 使用时将Lock加锁放在try-catch中,解锁放在failly中
线程通信
管程法
生产者消费者模型–>利用缓冲区:管程法
public class TestProductConsumer {
public static void main(String[] args){
Contact contact = new Contact();
new Product(contact).start();
System.out.println("开始做鸡");
new Consumer(contact).start();
}
}
class Product extends Thread{
Contact contact;
public Product(Contact contact) {
this.contact = contact;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
contact.makeUp(new Chicken(i));
System.out.println("生产了第"+i+"只鸡");
}
}
}
class Consumer extends Thread{
Contact contact;
public Consumer(Contact contact) {
this.contact = contact;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了第"+contact.pop().id+"只鸡");
}
}
}
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
class Contact {
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void makeUp( Chicken chicken){
if (chickens.length == count){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
this.notifyAll();
}
public synchronized Chicken pop(){
if (count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
}
}
信号灯法
标志位解决–>信号灯
public class TestPc2 {
public static void main(String[] args) {
Tv tv = new Tv();
new player(tv).start();
new Watcher(tv).start();
}
}
class Tv {
String name;
boolean flag = false;
public synchronized void play(String name) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
System.out.println("演员表演" + name);
this.notifyAll();
this.flag = !flag;
}
public synchronized void watched(){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众收看了" + this.name);
this.notifyAll();
this.flag = !flag;
}
}
class player extends Thread{
Tv tv;
public player(Tv tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if ( i % 2 == 0){
this.tv.play("快乐大本营");
}else {
this.tv.play("抖音:记录美好生活");
}
}
}
}
class Watcher extends Thread{
Tv tv;
public Watcher(Tv tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watched();
}
}
}
线程池
- 频繁的的创建和销毁线程需要大量的资源,在并发的前提下对性能的影响很大
- 解决提前创建很多线程,放在线程池中,使用时直接获取,用完放回池中,可以避免频繁的创建和销毁实现重复利用
- 好处:
- 提高响应速度(减少创建的新线程的时间
- 降低了资源的消耗
- 便于管理线程
- corePoolSize:核心池大小
- maximumPoolSize:最大线程数
- KeepAliveTime:线程没有任务时最多保持多长时间后会终止
- ExecutorService:真正的线程池接口,常见的子类ThreadPoolExecutor
- void execute(Runable command):执行任务/命令,没有返回值。用来执行Runable
- Future submit(Callable task):执行任务,有返回值。一般用来执行Callable
- void shutdown():关闭线程池
- Executors:工具类、线程池的工厂类。用于创建并返回不同类型线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
繁的创建和销毁实现重复利用
- 好处:
- 提高响应速度(减少创建的新线程的时间
- 降低了资源的消耗
- 便于管理线程
- corePoolSize:核心池大小
- maximumPoolSize:最大线程数
- KeepAliveTime:线程没有任务时最多保持多长时间后会终止
- ExecutorService:真正的线程池接口,常见的子类ThreadPoolExecutor
- void execute(Runable command):执行任务/命令,没有返回值。用来执行Runable
- Future submit(Callable task):执行任务,有返回值。一般用来执行Callable
- void shutdown():关闭线程池
- Executors:工具类、线程池的工厂类。用于创建并返回不同类型线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
threadPool.execute(new MyThread());
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}