theme: channing-cyan
highlight: androidstudio

文章目录

    • theme: channing-cyan highlight: androidstudio
  • 前言
    • 继承Thread
    • 实现Runale接口
    • Callable接口
    • 线程周期
    • CPU调度
  • 线程安全问题(并发问题)
    • 方案一,直接加synchronized
    • 提取Tickets
    • 上锁
    • 生成者与消费者
      • 信号灯
      • 管程法
  • 线程池

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」

前言

关于java线程的创建这里就不进行过多复述了

继承Thread

这个是直接使用那个Thread继承一下,重写run方法的

public class ThreadDome1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}public static void main(String[] args) {new ThreadDome1().start();for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}
}

实现Runale接口

还有一个是实现这个接口的。

public class RunableDome implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}public static void main(String[] args) {RunableDome run = new RunableDome();new Thread(run, "小明").start();new Thread(run,"小红").start();}}

这个有什么好处就不用多说了(多个线程可以控制一个对象),有什么问题(并发)才谁我们后面的重点之一。

Callable接口

这玩意和runable类似,但是的话有返回值,然后我们用线程池跑了一下。

public class CallabelDome implements Callable<Boolean> {@Overridepublic Boolean call() throws Exception {for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}return true;}public static void main(String[] args) {CallabelDome t1 = new CallabelDome();CallabelDome t2 = new CallabelDome();ExecutorService executorService = Executors.newFixedThreadPool(2);Future<Boolean> submit = executorService.submit(t1);Future<Boolean> submit1 = executorService.submit(t2);executorService.shutdownNow();}}

线程周期

这个看一张图即可明白

中文图

这里提一下先前那个线程停止的方式,我们一般是使用自己定义的方式(在外部停止的话)

public class RunableDome implements Runnable{private boolean flag = true;public void setFlag(boolean flag) {this.flag = flag;}public void stop(){this.setFlag(false);System.out.println("主线程调用stop让该线程停止运行");}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {if(!flag)return;System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}public static void main(String[] args) {RunableDome run = new RunableDome();new Thread(run,"子线程").start();for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");if(i==950){run.stop();}}}}

CPU调度

这个主要是关于线程一些,优先级,优先执行,调度转让的内容。

一个是线程转让

Thread.yield() 

就可以让 A 线程转让(假设在A线程执行这个代码)让CPU去调度别的线程,当然不一定成功。

Thread.setPriority(2)

这个就是那个优先级的那个,谁先执行,0到10 最大是10,当然这个也是得看cpu心情

还有一个设置守护线程,也就是主线程结束子线程就结束

我就使用了一个setDaemo()

public class RunableDome implements Runnable{private boolean flag = true;public void setFlag(boolean flag) {this.flag = flag;}public void stop(){this.setFlag(false);System.out.println("主线程调用stop让该线程停止运行");}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {if(!flag)return;System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}public static void main(String[] args) {RunableDome run = new RunableDome();Thread thread = new Thread(run, "子线程");thread.setDaemon(true);thread.start();for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName()+"运行了:"+i+"次");}}}

可以看到我们的子线程要跑10000次的结果只跑了1000多次,因为主线程结束了。

还有最后一个join强制获取CPU调度权,还是刚刚那个例子,我就要让子线程执行玩。
那么我就可以这样

线程安全问题(并发问题)

现在开始进入到我们应该开始关注的问题了。

首先我们举个买票的例子,这个也是很多人都说过的例子,以前我学python写的博客的时候使用的什么工厂的例子,那是是我自己相的,但是有点抽象,java的说那个的时候,我直接上代码,当时就是给自己看到呗,现在还是要考虑一下不仅自己要看得明白,别人也得更好看一点。

先上代码,这个例子很简单。

public class BuyTicket implements Runnable{private int tickets = 1000;//一开始1000张票@Overridepublic void run() {for (int i = 0; i < 1000; i++) {tickets --;System.out.println(Thread.currentThread().getName()+"买了第:"+(i+1)+"张票");}}public static void main(String[] args) {Runnable tickets = new BuyTicket();new Thread(tickets,"小明").start();new Thread(tickets,"小红").start();new Thread(tickets,"小容").start();}
}

看输出结果我们发现输出了三次936,也就是产生了错乱,也就是不安全。

原理的话其实很简单。我们可以举个例子模拟代码运行原因。

所以我们现在要解决这个问题。

我们在放大一下问题。用那个sleep放大一下问题。

public class BuyTicket implements Runnable{private int tickets = 10;//一开始1000张票@Overridepublic void run() {for (int i = 0; i < 10; i++) {tickets --;System.out.println(Thread.currentThread().getName()+"买了第:"+(i+1)+"张票还剩下"+tickets+"张票");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Runnable tickets = new BuyTicket();new Thread(tickets,"小明").start();new Thread(tickets,"小红").start();new Thread(tickets,"小容").start();}
}

方案一,直接加synchronized

我们现在直接看这个代码

public class BuyTicket implements Runnable{private int tickets = 10;//一开始1000张票@Overridepublic synchronized void run() {for (int i = 0; i < 10; i++){if(isEmpty(tickets))return;System.out.println(Thread.currentThread().getName()+"买了第:"+(i+1)+"张票还剩下"+tickets+"张票");tickets --;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public int getTickets() {return tickets;}public boolean  isEmpty(int tickets){if(tickets<=0){return true;}return false;}public static void main(String[] args) throws InterruptedException {BuyTicket tickets = new BuyTicket();Thread t1 = new Thread(tickets, "小明");Thread t2 = new Thread(tickets, "小红");Thread t3 = new Thread(tickets, "小容");t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();System.out.println(tickets.getTickets());}
}

加上这个synchronized其实就是相当于加了一把锁,但是问题解决了不。
我们可以看看结果

可以看到我们得到了一个想要的值,显然我们的这个方式是OK的,那好我们再看一下执行的时间。

花费了1秒。(这里其实还有个小细节其实我们可以发现这个sleep其实是不会释放掉当前线程的锁的,除非你强行释放了)

但是这里明显有一个问题那就是我们发现,这个是直接锁代码块(默认锁的是当前对象也就是this)那么就有问题了,你锁住了,10张票都被一个人买走了,显然是不行的,所以我们干嘛,得去想办法解决这个问题,那就是我们发现这我们需要上锁的对象其实一直都是只有一个家伙,那就是ticket,所以我们其实是可以直接对ticket进行同步的,也就是把这个家伙提取出来,因为这个synchornized需要接受对象,或者Class。

提取Tickets

class Ticket{private int tickets = 10;public int getTickets() {return tickets;}public void setTickets(int tickets) {this.tickets = tickets;}public void sell(int ticket){System.out.println(Thread.currentThread().getName()+"买了第:"+(ticket)+"张,票还剩下"+getTickets()+"张");setTickets(getTickets()-1);}
}public class BuyTicket implements Runnable{private Ticket tickets = new Ticket();//一开始1000张票@Overridepublic  void run() {for (int i = 0; i < 10; i++) {synchronized (tickets) {if (tickets.getTickets() <= 0) {return;}tickets.sell((i + 1));try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public static void main(String[] args) throws InterruptedException {BuyTicket tickets = new BuyTicket();Thread t1 = new Thread(tickets, "小明");Thread t2 = new Thread(tickets, "小红");Thread t3 = new Thread(tickets, "小容");long l = System.currentTimeMillis();t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();long l1 = System.currentTimeMillis();System.out.println("此时的票数为:"+ tickets.tickets.getTickets());System.out.println("执行时长为"+(l1-l));}
}

这个执行时长也差不多也是一样的。

上锁

我们使用代码synchronized代码块的话多多少少还是有点不方便的。所以我们和python一样可以上个锁。

我们回到最开始的代码,直接上锁。

public class BuyTicket implements Runnable{private int tickets = 10;//一开始1000张票private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {lock.lock();if (tickets <= 0) return;tickets--;System.out.println(Thread.currentThread().getName() + "买了第:" + (i + 1) + "张票还剩下" + tickets + "张票");}finally {lock.unlock();}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}public int getTickets() {return tickets;}public static void main(String[] args) throws InterruptedException {BuyTicket tickets = new BuyTicket();Thread t1 = new Thread(tickets, "小明");Thread t2 = new Thread(tickets, "小红");Thread t3 = new Thread(tickets, "小容");long l = System.currentTimeMillis();t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();long l1 = System.currentTimeMillis();System.out.println("此时的票数为:"+ tickets.getTickets());System.out.println("执行时长为"+(l1-l));}
}

接下来是执行时间这里我强行放锁了。

404 ms

如果我不放的话(睡眠后的话)

生成者与消费者

前面两个主要是说了synchronized,这个东西有点“重”所以我们用了锁,但是这个锁是比较耗费资源的,所以我们还有这个模式,这个也是我写那个原来java多线程的内容不仔细的地方,因为一开始我的案例就是用这个的,原因也是在python里面说了很多次,但是其实python里面有个东西我没说到,那就是协程,在python里面是有协程的,不过本质上还是一个单线程,这个和Go语言的协程有别开来,那个go语言的协程是指一个模式,那个其实是多线程,而且用那个管道思想,让Go的线程实现不仅简单而且非常快速的解决了同步通信的问题,很不错!有时间可以聊聊,Go也是个很不错的语言,只是目前我想做的事情Go给的支持还不够。

OK 那么现在回到我们这里,我们说生产者消费者模式,那么其实这里具体的实现大致有两种思路,一种就是和python一样用队列,这个叫做管程法,还有就是用标志,叫做信号灯法,这里我都会有说明,不要着急,毕竟我也不想去翻翻老博客。

当然其实从代码上面说的话,这个和我们的锁是类似的。只是这里使用了另一个玩意,叫做wait()

这里你可能会好奇这个wait和unlock()的区别,我这里直接说一些结论:

  1. 首先二者都是释放资源。
  2. 前者用于线程通信,wait()之后配置notify使用。
  3. 而lock的作用是保护多线程访问的共享资源
  4. 线程之间的通信方法Condition.await 和 Condition.signal (Object.wait 和 Object.notify) 的调用前提是必须获取到锁,如果没有获取到锁就直接调用,则会抛出java.lang.IllegalMonitorStateException 异常
  5. A线程调用 Lock.lock() 导致线程阻塞,如果B线程调用 Lock.unlock() 则自动会解除A线程的阻塞状态,该机制是JVM调度器来唤醒,不需要B线程显示的唤醒A
  6. A线程调用了 Condition.await(Object.wait),如果要唤醒A线程,那么B线程必须做到:
  1. A 获取到锁(因为A线程wait释放了锁)
  2. B 调用 Condition.signal(Object.notify),这个时候A还不能执行,只是被唤醒,唤醒后需要重新获取锁
  3. C 调用unlock释放锁,只有B线程释放了锁,A线程才能真正的被唤醒,然后获取到锁执行

信号灯

先来我们的信号灯法,这个实现比较快。同时也是使用我们这个wait()的。
首先我们要有消费者,生产者,以及产品
在我们这里消费者就是顾客,生产者就是售票处,产品自然就是票
这个直接上代码,比较简单。


class Ticket{public int tickets=0; //票的数量private boolean flag = false;//还有票不public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public synchronized void buy() throws InterruptedException {if(!isFlag()){wait();}tickets --;notifyAll();this.flag = !this.flag;}public synchronized void make() throws InterruptedException {if(isFlag()){wait();}tickets ++;notifyAll();this.flag = !this.flag;}
}class Salesman extends Thread{Ticket ticket;public Salesman(Ticket ticket) {this.ticket = ticket;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {ticket.make();} catch (InterruptedException e) {e.printStackTrace();}}}
}class Customer extends Thread{Ticket ticket;public Customer(Ticket ticket) {this.ticket = ticket;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {if(ticket.tickets<=0){return;}ticket.buy();System.out.println(Thread.currentThread().getName() + "买了第:" + (i + 1) + "张票还剩下" + ticket.tickets + "张票");Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}class Test{public static void main(String[] args) throws InterruptedException {Ticket ticket = new Ticket();Salesman salesman = new Salesman(ticket);Customer customer1 = new Customer(ticket);Customer customer2 = new Customer(ticket);salesman.setName("售票处");customer1.setName("小容");customer2.setName("小侯");long start = System.currentTimeMillis();salesman.start();customer1.start();customer2.start();salesman.join();customer1.join();customer2.join();long end = System.currentTimeMillis();System.out.println("执行了"+(end-start)+"毫秒还有票数:"+ticket.tickets);}
}

那么这就是我们的这个信号灯法。

管程法

这个也是,不过要稍微麻烦一点,不过可以直接使用synchronized实现,也不难。

class OneTicket{public OneTicket(int id) {this.id = id;}private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}
}class TicketContainer{OneTicket[] tickets = new OneTicket[10];//10个票int count = 0;public synchronized void push(OneTicket ticket) throws InterruptedException {if(count==tickets.length){wait();}tickets[count] = ticket;count++;notifyAll();}public synchronized OneTicket pop() throws InterruptedException {if(count==0){wait();}count--;OneTicket ticket = tickets[count];notifyAll();return ticket;}}class Peopel extends Thread{private TicketContainer ticketContainer;public Peopel(TicketContainer ticketContainer) {this.ticketContainer = ticketContainer;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(100);OneTicket pop = ticketContainer.pop();System.out.println(Thread.currentThread().getName() + "买了第:" + pop.getId() + "张票还剩下" + ticketContainer.count + "张票");} catch (InterruptedException e) {e.printStackTrace();}}}
}class SalesPeopel extends Thread{private TicketContainer ticketContainer;public SalesPeopel(TicketContainer ticketContainer) {this.ticketContainer = ticketContainer;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("生成了第"+(i+1)+"张票");try {ticketContainer.push(new OneTicket((i+1)));} catch (InterruptedException e) {e.printStackTrace();}}}
}class TestDome{public static void main(String[] args) throws InterruptedException {TicketContainer ticketContainer = new TicketContainer();SalesPeopel salesman = new SalesPeopel(ticketContainer);Peopel customer1 = new Peopel(ticketContainer);Peopel customer2 = new Peopel(ticketContainer);salesman.setName("售票处");customer1.setName("小容");customer2.setName("小侯");salesman.start();customer1.start();customer2.start();}
}

这里注意的是那个终止条件的问题(我没加),所以的话会一值堵塞。

此外:
这里的话我其实是有一个案例的,不过是使用python的多线程爬虫写的,爬取小姐姐的图片来者。代码是这个,理论都是一样的,只是我们这边使用的queue是人家写好的堵塞的玩意。

import requests
from lxml import etree
import threading
import os
from urllib import request
from queue import Queue
url_frist_save=Queue(1000)
#url_save=Queue(200)
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}url_root='http://pic.netbian.com'
def get_url():date_frist=requests.get(url_root,headers=headers)date_frist=date_frist.content.decode('gbk')date_frist_=etree.HTML(date_frist)uel_1=date_frist_.xpath('//li//a[@href and @title and @target]/@href')[4:]for i in uel_1:url=url_root+iurl_frist_save.put(url)for i in range(2,101):urlx='http://pic.netbian.com/index_{name}.html'.format(name=str(i))date_frist = requests.get(urlx, headers=headers)date_frist = date_frist.content.decode('gbk')date_frist_ = etree.HTML(date_frist)uel_1 = date_frist_.xpath('//li//a[@href and @target]/@href')[1:]for i in uel_1:url = url_root + iurl_frist_save.put(url)def down_load():if not  os.path.exists(r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures'):os.makedirs(r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures')while True:date_frist=requests.get(url_frist_save.get(),headers=headers)date_frist=date_frist.content.decode('gbk')date_frist_=etree.HTML(date_frist)url_down=url_root+str(date_frist_.xpath('//div[@class="photo-pic"]/a/img/@src')[0])name=str(date_frist_.xpath('//div[@class="photo-pic"]/a/img/@title')[0])path=r'C:\Users\a3139\Desktop\projects\爬虫dome\pictures'+'\\'+name+'.jpg'print(path)print(url_down)try:request.urlretrieve(url_down,path)request.urlcleanup()except:print('此图片下载失败')if url_frist_save.empty():print(url_frist_save.empty())breakif __name__=="__main__":for i in range(5):t1=threading.Thread(target=get_url)t2=threading.Thread(target=down_load)t1.start()t2.start()

应该还能用。

此外的话还有这个线程池,这个就是方便管理,我先前还记得有关一个线程池的面试题,关于线程池的七个参数,好像是,这个就今天早上说一下吧。

线程池

这个其实一开始有我们的演示。

public class Pools {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(3);executorService.execute(new MyThread());executorService.execute(new MyThread());executorService.execute(new MyThread());executorService.shutdownNow();//用完要关}
}class MyThread implements Runnable{@Overridepublic void run() {System.out.println("FUcking");}
}

这个是最简单的固定大小的,实际上我们有动态的,这个是一个考点,也是个小细节,毕竟要压榨资源呗~
早上再补充喽,那么计划的第一部分就OK了。

java多线程(分分钟基础秒杀)相关推荐

  1. java多线程并发基础汇总一

    一.java并发线程基础 文章目录 一.java并发线程基础 1.1 什么是线程 1.2 线程的创建方式 1.3 Object类中的方法:线程通知与等待 1.4 Thread类中的方法 1.4.1 等 ...

  2. Java多线程 ——线程基础和锁锁锁

    Java多线程(一) 一.线程的定义 二.Synchronize线程同步 三.偏向锁.自旋锁.重量级锁 四.volatile关键字 4.1.普通变量运算的物理意义 4.2.有无解决的方案 4.3.vo ...

  3. Java多线程从基础到并发模型统统帮你搞定

    前言 疫情过去,真正的春暖花开又回来了,时不时的可以和朋友约个饭,感慨今年的工作竞争压力很大,工作很不好找.作为一个开发人员,你是否面上了理想的公司,拿到了理想中的薪资? 作为程序员,跳槽就是最好的涨 ...

  4. java多线程编程--基础篇

    一.基本概念 a.操作系统中进程与线程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间, 一个进程中可以启动 ...

  5. 给我十分钟带你过完java多线程所有基础知识

    目录: 1.并发并行与线程进程 2.认识CPU和主线程 3.多线程的三种创建方式 4.三种创建多线程方式的进一步探究和对比 5.匿名内部类的多线程创建 6.多线程内存的分析 7.深度了解线程run() ...

  6. 高并发下Java多线程编程基础

    摘要: Java线程同步与异步 线程池 无锁化的实现方案 分布锁的实现方案 分享的目的: 进一步掌握多线程编程和应用的技巧,希望对大家在平时的开发中应对高并发编程有所帮助 Java线程同步与异步 1. ...

  7. java多线程编程基础

    thread和runnable已经out了.取而代之的是callable<V>,它的结果存在future<V>中.后者有get对象可以阻塞并最终获得异步结果.FutureTas ...

  8. java 多线程系列基础篇(二)

    概要 本章,我们学习"常用的实现多线程的2种方式":Thread 和 Runnable. 之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池 ...

  9. Java多线程系列--“基础篇”10之 线程优先级和守护线程

    概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括: 1. 线程优先级的介绍 2. 线程优先级的示例 3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/s ...

最新文章

  1. Cassandra读写性能测试
  2. oracle索引块和数据块,Oracle中,如何确定热快是数据块还是索引快?
  3. 腾讯如何打造新基建时代高可扩展的区块链引擎
  4. Java高质量代码之 — 泛型与反射
  5. cfree运行程序错误_C/C++程序调试和内存检测
  6. 二、Get和Post的区别
  7. 游戏筑基开发之printf及利用一维数组输出杨辉三角
  8. Eratosthenes筛法
  9. vue实现上传图片并预览效果 html+css+js
  10. 打包labview程序
  11. html代码中数学公式,html中使用mathjax数学公式
  12. js原生往父元素中添加子元素
  13. php empty是什么意思,php empty 和空字符串区别
  14. 史上最全的数据库面试题,面试前刷一刷!
  15. guzzle php,windows系统下安装使用guzzle
  16. 基于java的学生信息管理系统(含源文件)
  17. Java-Tcp/Ip-CS控制台聊天应用Demo
  18. android中按钮右对齐,在Android中左右对齐双按钮布局
  19. 计算机类专科学校排名,2017计算机专科学校排名一览表
  20. 全球LTE终端市场的现状及未来

热门文章

  1. spring实现dubbo服务
  2. rewind java_Java ShortBuffer rewind()用法及代码示例
  3. 简单计算一下,发现炒房一点不划算
  4. maven 跳过单元测试打包
  5. Hbase的二级索引和RowKey的设计
  6. 软件开发中的角色分工
  7. 树莓派4 使用 SnowBoy 搭建热词唤醒
  8. 咸阳哪里学计算机,咸阳计算机进修学院
  9. ToggleButton的用法
  10. 梦幻手游服务器维护摆摊公示时间,梦幻西游手游摆摊交易优化 11号维护安卓多区合服...