JAVA多线程

程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。

线程是CPU调度和执行的单位。

创建一个新的执行线程有两种方法:一是将一个类声明为Thread子类。这个子类应该重写run方法,编写线程执行体,创建线程对象,调用start()方法启动线程。二是实现Runnable接口;实现run()方法,编写线程执行体;创建线程,调用start()启动线程。

线程开启不一定立即执行,看CPU的调度

创建方法一举例:

public class Threaddemo1 extends Thread{
@Override
public void run() {
//run方法线程体
for(int i = 0 ; i < 100 ; i++){
System.out.println("thread"+i);
}
} public static void main(String[] args) {
Threaddemo1 thread = new Threaddemo1();
thread.start(); for(int i = 0 ; i < 100 ; i++){
System.out.println("hello"+i);
}
}
}

输出结果是相互交替的,因为是CPU负责调度,每次结果都可能不一样,若把start()换成run()则顺序确定,性质就不同了。

创建方法二举例:

public class TestThread implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++){
System.out.println("thread"+i);
}
} public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread testThread = new TestThread();
//创建线程对象
Thread thread = new Thread(testThread);
thread.start(); for(int i = 0 ; i < 100 ; i++){
System.out.println("hello"+i);
}
}
}

两种方法稍微有差异,但是以上两段代码输出等效

总结

继承Thread类:子类继承Thread类具备多线程能力;启动线程只需要子类对象.start();不建议使用(避免OOP单继承局限性)

实现Runnable接口:启动线程需要传入目标对象+Thread对象.Strat();推荐使用(避免单继承局限性,灵活方便,方便同一个对象被多个线程使用)

模拟抢票

@Override
public void run() {
while(true){
if(Nums <= 0)
break;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--> get the No."+Nums--);
}
}

重写run(),该段实现每次间隔200毫秒,环境模拟抢票,main()如下

TestThread1 testThread1 = new TestThread1();

new Thread(testThread1,"xiaoming").start();
new Thread(testThread1,"xiaohong").start();
new Thread(testThread1,"huangniu").start();

该demo出现了不安全情况,可能同时出现两个线程抢占一个资源,考虑信号量方面,没有实现互斥。

模拟龟兔赛跑

public class race implements Runnable{

    private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
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 race1 = new race(); new Thread(race1,"兔子").start();
new Thread(race1,"乌龟").start();
}
}

在这个demo中发现,若不建立公共变量,是不会出现抢占资源的情况的。

实现Callable接口(简单了解)

可以定义返回值,可以抛出异常

  1. 需要定义返回值类型
  2. 重写call方法,若获取结果需抛出异常
  3. 创建目标对象
  4. 创建执行服务
  5. 提交执行
  6. 获取结果
  7. 关闭服务

举例一个demo,首先介绍定义的内部类

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 Exception,downloader has a problem");
}
}
}

该类实现的是将一个url文件下载下来

public class TestCallable implements Callable <Boolean>{
private String url;
private String name;
public TestCallable(String url,String name){
this.url = url;
this.name = name;
} @Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("download the file named "+name);
return true;
} public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable s = new TestCallable("URL.jpg","wallpaper1.jpg");
TestCallable s1 = new TestCallable("URL.jpg","wallpaper2.jpg");
TestCallable s2 = new TestCallable("URL.jpg","wallpaper3.jpg");
//创建执行服务,线程池
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> rs1 = ser.submit(s);
Future<Boolean> rs2 = ser.submit(s1);
Future<Boolean> rs3 = ser.submit(s2);
//获取结果
Boolean rt1 = rs1.get();
Boolean rt2 = rs2.get();
Boolean rt3 = rs3.get(); //关闭服务
ser.shutdown();
}
}

以上只是简单的应用,将在下面的文章中介绍具体用法。

静态代理

真实对象和代理对象都要实现同一个接口

代理对象要代理真实角色

好处:代理对象可以做很多真实对象做不了的事情;真实对象专注于做自己的事情

interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{ @Override
public void HappyMarry() {
System.out.println("its very happy.");
}
}
//代理角色
class WeddingCompany implements Marry{
private Marry target; public WeddingCompany(Marry target){
this.target = target;
} @Override
public void HappyMarry() {
before();
this.target.HappyMarry();//真实对象结婚
after();
} private void before(){
System.out.println("布置");
} private void after(){
System.out.println("收款");
}
}

以结婚的过程举例静态代理,其实Thread就是一个代理角色,真实角色则为Runnable

Lambda表达式

为了避免内部类定义过多

(params)-> expression(表达式); (params) -> statement(语句); (params) -> {statements}

函数式接口的定义:

  • 任何接口,如果只包含唯一一个抽象方法,那么他就是函数式接口
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
like = ()->{
System.out.println("I like lambda2");
};
like.lambda();

like为接口定义的一个对象,此接口中有一个lambda方法。

interface Ilike{
void lambda(int a);
}

三种简化写法:

like = (a)->{
System.out.println("I like lambda"+a);
}; like = a -> {
System.out.println("I like lambda"+a);
}; like = a -> System.out.println("I like lambda"+a);
like.lambda(520);

Lambda表达式前提是接口为函数式接口;多个参数可以同时去掉参数类型,需要保持同步,需要加小括号;

简化过程其实可以理解为:静态内部类(类中定义static class,重写方法)->局部内部类(类中定义class,重写方法)->匿名内部类(直接new 接口,重写方法,不写内部类名)->Lambda表达式

线程状态

  • 创建状态
  • 就绪状态

start()之后线程进入就绪状态,但不意味着立即调度执行

  • 阻塞状态
  • 运行状态
  • 死亡状态

线程中断或者结束,一旦进入死亡状态,就不能再次启动

Thread常用方法:

方法 作用
setPriority(int newPriority) 更改此线程的优先级。
static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
void join() 等待这个线程死亡。
static void yield() 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。
void interrupt() 中断这个线程。
boolean isAlive() 测试这个线程是否活着。
  • 一般来讲线程最好正常停止(利用循环次数,不建议死循环)

  • 建议设置一个标志位

State枚举类型(具有约束,例如定义四季枚举类型:春夏秋冬)

Thread.State state = thread.getState();

线程休眠_sleep

  • sleep指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后,线程进入就绪状态
  • 可以模拟网络延时,倒计时等
  • 每个对象都有一个锁,sleep不会释放锁
  • sleep() 方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指 this.currentThread() 返回的线程。
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

需要抛出异常

模拟网络延时:可以放大问题的发生性(例如上方“模拟抢票”中,设置一定休眠时间,会发现多个线程抢占同一公共资源)

模拟倒计时:

public static void main(String[] args) throws InterruptedException {
//Date startTime = new Date(System.currentTimeMillis()); //获取系统当前时间
while(true){
Thread.sleep(1000);
Date startTime = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
}
}

每隔1s输出一下当前时间(Date类的使用)。

线程礼让_yield

  • 让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}

执行两个线程,结果如下:

0执行,礼让后给1。该结果可能不成功。yield执行后,会让CPU重新调度,重新调度的结果可能是新的也可能是旧的。

线程强制执行_join

合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(本质插队)

public class testJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("special Thread comes."+i);
}
} public static void main(String[] args) throws InterruptedException {
testJoin t = new testJoin();
Thread t1 = new Thread(t);
t1.start(); for (int i = 0; i < 500; i++) {
if(i==200){
t1.join();
}
System.out.println("main"+i);
}
}
}

结果为,在循环的500中,前200遍历主线程和t1在并行,而在200时,只剩下t1执行,执行完毕后才把资源归还主线程。

后续将在下一节中介绍线程优先级,锁,同步,生产者消费者,线程池,管程等相关问题。

简述Java多线程(一)的更多相关文章

  1. 简述Java多线程(二)

    Java多线程(二) 线程优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行. 优先级高的不一定先执行,大多数情况是这样的. 优 ...

  2. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  3. Java多线程原理+基础知识(超级超级详细)+(并发与并行)+(进程与线程)1

    Java多线程 我们先来了解两个概念!!!! 1.什么是并发与并行 2.什么是进程与线程 1.什么是并发与并行 1.1并行:两个事情在同一时刻发生 1.2并发:两个事情在同一时间段内发生 并发与并行的 ...

  4. Java多线程:Linux多路复用,Java NIO与Netty简述

    JVM的多路复用器实现原理 Linux 2.5以前:select/poll Linux 2.6以后: epoll Windows: IOCP Free BSD, OS X: kqueue 下面仅讲解L ...

  5. Java多线程小结

    简述 Java是支持多线程编程的语言,线程相比于进程更加轻量级,线程共享相同的内存空间,但是拥有独立的栈.减少了进程建立.销毁的资源消耗.jdk1.5后对java的多线程编程提供了更完善的支持,使得j ...

  6. Java多线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  7. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  8. Java多线程面试题整理

    部分一:多线程部分: 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. ...

  9. Java多线程:AQS

    在Java多线程:线程间通信之Lock中我们提到了ReentrantLock是API级别的实现,但是没有说明其具体实现原理.实际上,ReentrantLock的底层实现使用了AQS(AbstractQ ...

随机推荐

  1. 千万不要错过VAST,NGK算力的下一个财富机会!

    我们把目光投向NGK市场,近来,NGK接连新币,推出了SPC后,又有VAST.在目前市场上债券收益率已经趋近于零的情况下,世界上的大多数央行都在试图让本国货币贬值,所以在此时寻找其他保值资产是合理的. ...

  2. 01、初识Java

    目录 前言 一.认识Java 历史介绍 Java介绍 二.认识及安装JDK 1.认识JDK 2.安装JDK 配置与测试 配置注意及不生效解决 3.认识Java虚拟机 三.Java的工作方式 四.jav ...

  3. eclipse安装mybatis的插件

    在help中打开Eclipse Marketplace... 输入mybatis后搜索,点击install即可 功能说明: 1.查找某一个方法 在dao接口中 按住Ctrl键,鼠标指到方法名称上 选择 ...

  4. C#日志使用

    本文参考链接 日志框架 框架选择:NLog 安装方法,Nuget命令行:Install-Package NLog 常用规则 尽量不要在循环中打印日志. 应输出错误的堆栈信息:e.Message仅为异常 ...

  5. MarkDown简单语法回顾

    写在前面: 本文是我的第一篇博客文章,希望与大家共同交流.分享我们热爱的技术,努力成为一名优秀的软件工程师! 进入正文 使用MarkDown记笔记已经有些时候了,编辑器是使用的sublime text ...

  6. nginx反向代理、负载均衡以及分布式下的session保持

    [前言]部署服务器用到了nginx,相比较于apache并发能力更强,优点也比其多得多.虽然我的项目可能用不到这么多性能,还是部署一个流行的服务器吧! 此篇博文主要学习nginx(ingine x)的 ...

  7. 500GJava/Hadoop/Spark/机器学习...视频教程免费分享 百度云持续更新

    参加工作这么长时间了,工作中遇到了不少技能都是看视频教程学习的,相比较看书而言看视频确实比较容易理解.分享一下自己看过的和收集的视频教程. 资源包括: 大数据方面的Hadoop(云帆,小象学院,八斗学 ...

  8. kubernets资源预留

    一.  Kubelet Node Allocatable Kubelet Node Allocatable用来为Kube组件和System进程预留资源,从而保证当节点出现满负荷时也能保证Kube和Sy ...

  9. python基础(8)python中is和==的区别详解

    前置知识点 当我们创建一个对象时,我们要知道它内部干了些什么 1.创建了一个随机id,开辟了一片内存地址 2.自动声明了这个对象的类型type 3.给这个对象赋值value 小例子 a = 1 pri ...

  10. 【Azure API 管理】APIM CORS策略设置后,跨域请求成功和失败的Header对比实验

    在文章"从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能"中分析了CORS返回空200的问题后,进一 ...