一、引言

说到线程,经常会听到线程同步,首先是为什么要线程同步?什么是线程同步?

因为当有多个线程要同时访问一个变量或对象时,如果这些线程的执行(比如既有读又有写操作)时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。这里以在银行账户取钱为例,工行的卡里有¥3000,此时微信和支付宝做查询的时候,均显示3000元,如果微信支付2000,支付宝支付2000

这里就会使用到多线程。在使用之前先了解下多线程的的知识

什么时候需要使用到多线程?

程序需要同时执行两个或者多个任务时,如用户输入,文件读写等。Java语言的JVM运行程序运行多个线程,通过java.lang.Thread类来实现。

Thread有如下的特性:

  • 每个线程都是通过某个特定的 Thread对象的run()方法来完成操作的, 经常把run()的主体称为线程体
  • 通过Thread类的start()方法来调用这个线程

因此想要在开启的多线程中运行代码逻辑,就写在run()即可。Thread有如下的构造函数

Thread();//创建Thread对象
Thread(String threadName);//创建线程并指定线程实例名称
Thread(Runnable target);//指定创建线程的目标对象,它实现了Runnable接口中的run方法
Thread(Runnable target,String name);//创建新的Thread对象

1、线程创建

而创建线程通常有两种方式:

1.1、继承Thread类

1)定义子类继承Thread类

2)子类中重写Thread类的run方法

3)创建Thread子类对象,即创建线程对象

4)调用线程对象start()方法,启动线程,调用run()方法

先看一下Test.java

package blog;public class Test {public static void main(String[] args) {Thread t0 = new TestThread();t0.start();//主线程System.out.println("主线程========");System.out.println("主线程========");System.out.println("主线程========");}
}

继承Thread的子类为TestThread.java

package blog;public class TestThread extends Thread{@Overridepublic void run() {// TODO Auto-generated method stub//super.run();System.out.println("运行多线程的代码");for (int i = 0; i < 5; i++) {System.out.println("多线程逻辑 : " + i);}}
}

这里不会给出上述的运行结果,因为每次运行可能输出结果不同。这是因为在mian中执行t0.start()就开启了多线程,这个时候t0.start()之后的main方法中的其他代码的运行和就run方法的运行没有关联了。因此控制台输出的结果为子线程输出结果和main中的输出结果的并集,且顺序不确定,但是各自回保持各自的先后顺序关系。这个就是多线程。

当然这里还可以继承新开新的线程

Thread t0 = new TestThread();
t0.start();Thread t1 = new TestThread();
t1.start();

1.2、实现Runnable接口

1)定义子类实现Runnable接口

2)子类中重写Runnable接口中的run方法

3)创建Thread类含参构造器创建线程对象

4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法

5)调用线程对象start()方法,启动线程,调用Runnable子类的run()方法

先在TestRunnable.java子类中实现Runnable接口

public class TestRunnable implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println("运行多线程的代码");for (int i = 0; i < 5; i++) {System.out.println("Runnable多线程逻辑 : " + i);}}}

同样在main中,创建对象,

这里有个Runnable实例

package blog;public class Test {public static void main(String[] args) {Thread t3 = new Thread(new TestRunnable());t3.start();//主线程System.out.println("主线程========");System.out.println("主线程========");System.out.println("主线程========");}
}

同样在上述的创建对象的过程还有一个两个参数的构造函数,Thread(Runnable target,String name),这里的name指定为线程的名称。注意这里使用的是new TestRunnable()匿名对象。

先将TestRunnable.java中的输出语句修改为:

System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + i);

这里的Thread.currentThread().getName()就是获取当前线程名称,输出对比发现,未设置的默认为Thread-0:

那这个有个命名有什么用呢,当然是可以查看多线程的之间的异步调用次序

既然实现多线程有这两种方式,那使用哪个呢?

2、多线程实现两种方式的区别

推荐使用实现Runnable的方式

  • 继承Thread,线程代码存放在Thread子类run方法中,重新run()方法
  • 实现Runnable,线程代码存在在实现接口子类的run()方法中,实现run()方法

实现接口的好处:

1)避免了单接口的限制

2)多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源

将上述代码修改下,就能体现出来Runnable实现的子类的作用。

package blog;public class TestRunnable implements Runnable{private int count = 0;@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println("运行多线程的代码");for (int i = 0; i < 5; i++) {count ++;System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + count);}}}

在main改造如下所示

t4和t5共同处理的是TestRunnable中的资源,count

3、Thread常用方法

3.1、线程启动设置

void start()启动线程,并执行run()方法

run()线程在被调度时执行的操作

String getName()返回线程的名称

static currentThread()返回当前线程

package blog;public class TestRunnable {public static void main(String[] args) {TestRun run0 = new TestRun();TestRun run1 = new TestRun();Thread t0 = new Thread(run0);Thread t1 = new Thread(run1);t0.start();t1.start();t0.setName("线程-t0");System.out.println(t0.getName());//如果没有设置线程名称,则系统会给出系统默认名称,为Thread-NSystem.out.println("主线程========");System.out.println("主线程========");}}class TestRun implements Runnable{private int count = 0;@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " :运行多线程的代码");for (int i = 0; i < 5; i++) {count ++;System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + count);}}
}

输出结果如下:

3.2、线程优先级设置

getPriority() 返回线程优先级

setPriority(int newPriority) 设置线程优先级,范围为[1,10]数字越大优先级约到,默认为5,设置较大优先级表示当前线程有较大概率被优先执行

从输出结果来看,并不是设置了最高优先级就一定是最先获得资源并进行执行,所以这里说的的有较高概率去执行

3.3、线程让步yield

让步顾名思义,就是“让给他人”,暂停当前正在执行的线程,把执行机会让给优先级相同的或者优先级更高的线程;若队列中没有相同优先级的线程,则忽略此步骤。

package blog;public class TestRunnable {public static void main(String[] args) {TestRun run0 = new TestRun();TestRun run1 = new TestRun();Thread t0 = new Thread(run0);Thread t1 = new Thread(run1);t0.start();t1.start();System.out.println(t0.getName());//如果没有设置线程名称,则系统会给出系统默认名称,为Thread-NSystem.out.println("主线程========");System.out.println("主线程========");}}class TestRun implements Runnable{private int count = 0;@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " :运行多线程的代码");for (int i = 0; i < 5; i++) {if(i % 2 == 0)Thread.yield();//线程让步count ++;System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + count);}}
}

在run()方法中,通过判断循环是否为偶数,来实现让线程让步。

3.4、线程阻塞

当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完成。通俗的说就是调用join的线程做了一次插队的操作,见如下所示

package blog;public class TestRunnable {public static void main(String[] args) {TestRun run0 = new TestRun();TestRun run1 = new TestRun();Thread t0 = new Thread(run0);Thread t1 = new Thread(run1);t0.start();t1.start();System.out.println("主线程========1");System.out.println("主线程========2");try {t0.join();//相当于把t0.run的代码插入到此处进行执行} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("主线程========3");}}class TestRun implements Runnable{private int count = 0;@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " :运行多线程的代码");for (int i = 0; i < 5; i++) {count ++;System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + count);}}
}

输出结果如下:

3.5、线程睡眠与结束

 @Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " :运行多线程的代码");for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}count ++;System.out.println(Thread.currentThread().getName() + " :Runnable多线程逻辑 : " + count);}}

强制结束线程使用t0.stop()即可,使用很少,这里不再赘述。

4、线程生命周期与状态

介绍了上面这么的线程的常用方法,那调用了对应的方法达到了什么样的线程状态呢?

Java多线程(1)—线程初探相关推荐

  1. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  2. Java多线程之线程池配置合理线程数

    Java多线程之线程池配置合理线程数 目录 代码查看公司服务器或阿里云是几核的 合理线程数配置之CPU密集型 合理线程数配置之IO密集型 1. 代码查看公司服务器或阿里云是几核的 要合理配置线程数首先 ...

  3. Java多线程之线程池的手写改造和拒绝策略

    Java多线程之线程池的手写改造和拒绝策略 目录 自定义线程池的使用 四种拒绝策略代码体现 1. 自定义线程池的使用 自定义线程池(拒绝策略默认AbortPolicy) public class My ...

  4. Java多线程之线程池7大参数、底层工作原理、拒绝策略详解

    Java多线程之线程池7大参数详解 目录 企业面试题 线程池7大参数源码 线程池7大参数详解 底层工作原理详解 线程池的4种拒绝策略理论简介 面试的坑:线程池实际中使用哪一个? 1. 企业面试题 蚂蚁 ...

  5. Java多线程之线程池详解

    Java多线程之线程池详解 目录: 线程池使用及优势 线程池3个常用方式 线程池7大参数深入介绍 线程池底层工作原理 1. 线程池使用及优势 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务 ...

  6. Java多线程之线程通信之生产者消费者阻塞队列版

    Java多线程之线程通信之生产者消费者传统版和阻塞队列版 目录 线程通信之生产者消费者传统版 线程通信之生产者消费者阻塞队列版 1. 线程通信之生产者消费者传统版 题目: 一个初始值为零的变量,两个线 ...

  7. Java多线程之线程虚假唤醒

    Java多线程之线程虚假唤醒 本文目录提纲 问题:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次. 问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线 ...

  8. Java多线程:线程安全和非线程安全的集合对象

    转载自  Java多线程:线程安全和非线程安全的集合对象 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到 ...

  9. java 多线程使用线程池_Java多线程:如何开始使用线程

    java 多线程使用线程池 什么是线程? (What is a Thread?) A thread is a lightweight process. Any process can have mul ...

  10. java多线程方式轮询,深入理解JAVA多线程之线程间的通信方式

    一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过sy ...

最新文章

  1. 启动ipython_ipython,_ipython 启动错误,ipython - phpStudy
  2. Django中多种重定向方法使用
  3. QT的QStack类的使用
  4. Go 语言编写 CPython 扩展 goPy
  5. 分享9个实用的电脑维修技巧,赶紧收藏吧!
  6. 使用HTML5中的Canves标签制作时钟特效
  7. delphi ehlib 添加选择框_教你用CASS10.1,在宗地图中添加“亩”注记
  8. stm32系统时钟配置,标准库v3.5
  9. Boblog热门日志、随机日志、热门Tags插件源代码
  10. 中国矿业大学计算机学院进复试,拟录取名单陆续公布!初试第二败北,倒数第一逆袭!...
  11. 百度url提交入口 百度网站收录提交入口网址
  12. html 蜂巢布局,css3类似蜂巢网格布局样式代码
  13. 卡内基梅隆计算机专业,卡内基梅隆大学计算机专业介绍
  14. Centos7系统安全漏洞及修复方案
  15. 百度竞价账户关键词怎么拓词
  16. JQuery获取文件大小
  17. jekyll-paginate 缺失
  18. 华南理工大学php,华南理工大学网络教育平台v3
  19. insmod: error inserting ‘./module1.ko‘: -1 Unknown symbol in module
  20. PIM-DM协议原理

热门文章

  1. HDU - 6183 暴力,线段树动态开点,cdq分治
  2. advanced installer重新打包教程
  3. 利用T-SQL语句创建数据表
  4. 详述JavaScript数组
  5. 【转】最小编辑距离 算法原理
  6. 习题11.6 fill_n
  7. 在EXE和DLL中,FindResource的区别
  8. 出块过程(2)nodeos 服务器接收消息
  9. 2021年MathorCupD题思路
  10. 设计模式--模板方法(Template Method)模式