Java多线程(1)—线程初探
一、引言
说到线程,经常会听到线程同步,首先是为什么要线程同步?什么是线程同步?
因为当有多个线程要同时访问一个变量或对象时,如果这些线程的执行(比如既有读又有写操作)时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。这里以在银行账户取钱为例,工行的卡里有¥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)—线程初探相关推荐
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
- Java多线程之线程池配置合理线程数
Java多线程之线程池配置合理线程数 目录 代码查看公司服务器或阿里云是几核的 合理线程数配置之CPU密集型 合理线程数配置之IO密集型 1. 代码查看公司服务器或阿里云是几核的 要合理配置线程数首先 ...
- Java多线程之线程池的手写改造和拒绝策略
Java多线程之线程池的手写改造和拒绝策略 目录 自定义线程池的使用 四种拒绝策略代码体现 1. 自定义线程池的使用 自定义线程池(拒绝策略默认AbortPolicy) public class My ...
- Java多线程之线程池7大参数、底层工作原理、拒绝策略详解
Java多线程之线程池7大参数详解 目录 企业面试题 线程池7大参数源码 线程池7大参数详解 底层工作原理详解 线程池的4种拒绝策略理论简介 面试的坑:线程池实际中使用哪一个? 1. 企业面试题 蚂蚁 ...
- Java多线程之线程池详解
Java多线程之线程池详解 目录: 线程池使用及优势 线程池3个常用方式 线程池7大参数深入介绍 线程池底层工作原理 1. 线程池使用及优势 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务 ...
- Java多线程之线程通信之生产者消费者阻塞队列版
Java多线程之线程通信之生产者消费者传统版和阻塞队列版 目录 线程通信之生产者消费者传统版 线程通信之生产者消费者阻塞队列版 1. 线程通信之生产者消费者传统版 题目: 一个初始值为零的变量,两个线 ...
- Java多线程之线程虚假唤醒
Java多线程之线程虚假唤醒 本文目录提纲 问题:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次. 问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线 ...
- Java多线程:线程安全和非线程安全的集合对象
转载自 Java多线程:线程安全和非线程安全的集合对象 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到 ...
- java 多线程使用线程池_Java多线程:如何开始使用线程
java 多线程使用线程池 什么是线程? (What is a Thread?) A thread is a lightweight process. Any process can have mul ...
- java多线程方式轮询,深入理解JAVA多线程之线程间的通信方式
一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过sy ...
最新文章
- 启动ipython_ipython,_ipython 启动错误,ipython - phpStudy
- Django中多种重定向方法使用
- QT的QStack类的使用
- Go 语言编写 CPython 扩展 goPy
- 分享9个实用的电脑维修技巧,赶紧收藏吧!
- 使用HTML5中的Canves标签制作时钟特效
- delphi ehlib 添加选择框_教你用CASS10.1,在宗地图中添加“亩”注记
- stm32系统时钟配置,标准库v3.5
- Boblog热门日志、随机日志、热门Tags插件源代码
- 中国矿业大学计算机学院进复试,拟录取名单陆续公布!初试第二败北,倒数第一逆袭!...
- 百度url提交入口 百度网站收录提交入口网址
- html 蜂巢布局,css3类似蜂巢网格布局样式代码
- 卡内基梅隆计算机专业,卡内基梅隆大学计算机专业介绍
- Centos7系统安全漏洞及修复方案
- 百度竞价账户关键词怎么拓词
- JQuery获取文件大小
- jekyll-paginate 缺失
- 华南理工大学php,华南理工大学网络教育平台v3
- insmod: error inserting ‘./module1.ko‘: -1 Unknown symbol in module
- PIM-DM协议原理