java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)
CPU:10核 主频100MHz
1核 主频 3GHz
那么哪一个CPU比较好呢?
CPU核不是越多越好吗?并不一定。主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运算货物快?显然是1架飞机,因此1核3GHz的CPU较好,当然,在相同主频的情况下,CPU当然是越多越好。
在Java中,JVM虚拟机允许运行多个线程,他通过java.lang.Thread类来实现
Thread类特性:
- 每个线程都是通过某个特定的Thread对象的run()方法来完成操作的,经常把run()方法主体称为线程体;
- 通过该Thread()对象的start()方法来调用这个线程;
构造方法:
- Thread():创建新的Thread对象;
- Thread(String threadname):创建线程并为其指定线程实例名;
- Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法;
- Thread(Runnable target,String name):创建新的Thread对象;
一、创建线程的两种方式:
1.继承Thread类
- 定义子类并继承Thread类
- 子类中重写Thread类中run方法
- 创建Thread子类对象,即创建了线程对象
- 调用线程对象的start方法:启动线程,调用run方法
package testThread;
public class TestThread extends Thread{
public void run() {
System.out.println("多线程运行的代码");
for(int i=0;i<10;i++) {
System.out.println("这是多线程的逻辑代码");
}
}
}
package testThread;
public class Test {
public static void main(String[] args) {
Thread t0 = new TestThread();
//在开启了线程后,线程与main()方法并行运行
t0.start();//启动线程
}
}
2.实现Runnable接口
- 定义子类,实现Runnable接口
- 子类中重写Runnable接口中的run方法
- 通过Thread类含参构造器创建线程对象
- 将Runanable接口的子类对象作为实际参数传递给Thread类的构造方法中
- 调用Thread类的start方法:开启线程、调用Runnable子类接口的run方法
package testThread;
public class TestRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"开始运行");
for(int i=0;i<10;i++) {
System.out.println("这是多线程的逻辑代码");
}
}
}
package testThread;
public class Test {
public static void main(String[] args) {
Thread t0 = new Thread(new TestRunnable());
t0.start();
Thread t1 = new Thread(new TestRunnable(),"线程t1");
t1.start();
}
}
对于这种方式创建线程,可以给每个线程进行命名,否则默认为Thread-num。
利用实现方式:避免了单继承的局限性、多个线程可以共享同一个接口实现类对象,非常适合多个相同线程来处理同一份资源。
二、Thread类的相关方法
(1)基础方法
void start():启动线程
run():线程在被调度时执行的操作名称
String getName():返回线程的名称
void setName(String name):设置线程的名称
static currentThread():返回当前线程
(2)优先级方法
- 数字越大,优先级越高:MAX_PRIORITY(10)、MIN_PRIORITY(1)、NORM_PRIORITY(5)
getPriority():获得优先级
setPriority(int newPriority):设置优先级
线程创建时继承父线程的优先级
(3)其它方法
static void yield():线程让步
- 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程;
- 若队列中没有同优先级的线程,忽略此方法;
join():当某个程序执行流中调用其它线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完毕为止。
- 低优先级的线程也可以获得执行
static void sleep(long millis):指定时间,毫秒
- 令当前活动线程在指定时间段内放弃对CPU控制,使其它线程有机会被执行,时间到后排队;
- 抛出InterruptedException异常
stop():强制线程生命周期结束
boolean isAlive():判断线程是否还活着
三、线程的生命周期
JDK中用Thread.State来表示线程的状态,包括:
新建:声明并实例化之后;
就绪:执行start之后;
运行:得到cpu的使用权,run()方法开始运行;
阻塞:run()方法停止执行,处于等待状态;
死亡:线程完成了全部工作或被提前终止;

四、线程的同步
问题:假设账户上有4000,现在有两个线程,分别各取2000,由于这两个线程是并行的,因此都可能取成功,此时账户上就是-1000了,这显然是不合法的。因此,要引入线程的同步,所谓同步,并不是指同时运行,而是指协同步伐,也就是线程按先后顺序依次执行,这样当取出2000后,账户剩余1000,再进行取2000就不会成功了。
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
User u_weChat = new User(account,);
User u_zhifubao = new User(account,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
public void get(int m) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
account.get();
}
}
输出:
正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
微信操作-取款金额:2000
微信操作-取款后的余额:-1000
此时,正如以上我们所说的,取款不合法了,那么如何解决呢?可以给方法加上同步锁。但是,需要注意的是:
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
Account account1 = new Account();
//意思是这里创建了不同的Account对象,获得的锁是不同对象的锁
User u_weChat = new User(account,);
User u_zhifubao = new User(account1,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
//在普通方法上加synchronized,锁的是整个对象,而不是某一个方法
//不同的对象就是不同的锁
public synchronized void wGet(int m) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
public synchronized void zGet(int m) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
if(Thread.currentThread().getName().equals("微信")) {
account.wGet(money);
}else {
account.zGet(money);
}
}
}
输出:
正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
微信操作-取款金额:2000
支付宝操作-账户原有金额:3000
微信操作-取款后的余额:1000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:-1000
因为此时不同对象获得的是不同的锁,所以这种方式并不行,那么如何进行改动呢?只需要使用同一个对象即可,即:
Account account = new Account();
User u_weChat = new User(account,);
User u_zhifubao = new User(account,);
此时输出:
正在运行:微信
微信操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
正在运行:支付宝
支付宝操作-账户金额不足:1000
同时,还可以进一步简化:
public synchronized void get(int m) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
在最后调用时,只需要在run()方法中调用account.get()即可,不用将两个方法分开来写。
获得锁的线程会执行完毕后,才将锁交给下一个线程继续执行。
对于在静态方法上加锁:
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
Account account1 = new Account();
//意思是这里虽然创建了两个不同的Account对象,但是获得的锁是同一个锁
User u_weChat = new User(account,);
User u_zhifubao = new User(account1,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
//静态方法加同步锁之后,对于所有的对象都是同一个锁
public static synchronized void get(int m) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
account.get(money);
}
}
对于这种方式,即使传入的是该类的不同对象,仍然获得的是同一个锁。
正在运行:微信
微信操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
正在运行:支付宝
支付宝操作-账户金额不足:1000
还有一种方式是利用同步锁修饰代码块:
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
Account account1 = new Account();
User u_weChat = new User(account,);
User u_zhifubao = new User(account1,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
public void get2(int m) {
//用this锁代码块是代表当前的对象,如果在其它方法中也有synchronized(this)的代码块用的是同一个同步锁
synchronized (this) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
account.get2(money);
}
}
输出:
正在运行:支付宝
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
正在运行:微信
微信操作-账户金额不足:1000
想要不同的对象有不同的锁:
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
Account account1 = new Account();
User u_weChat = new User(account,);
User u_zhifubao = new User(account1,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
public void get3(int m,Account a) {
//表示通过方法的参数传递进来的对象的代码块加了同步锁
//不同的对象有不同的同步锁
synchronized (a) {
String thread = Thread.currentThread().getName();
System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
account.get3(money,account);
}
}
输出:
正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
支付宝操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:-1000
此时,这种加锁方式就没有效果了,与最开始的那种是一样的。
总而言之,要想使加锁有效果,必须获得的是同一个对象的锁。
五、线程之间的通信
wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。
notify():唤醒正在排队等待同步资源的线程中优先级较高者结束等待。
notifyAll():唤醒正在排队等待资源的所有线程结束等待。
java.lang.Object提供的三个方法只有在sychronized方法或synchronized代码块中才能使用。
对于第四节提到到最后一种方式,只要将使用同一个Account对象,加锁机制就还是成功。但是我们会发现,之前的结果都是微信在支付宝之前进行操作,假设我们要让支付宝先操作,又应该怎么办呢?这时就需要利用线程之间的通信。
package testThread;
public class Test2 {
public static void main(String[] args) {
Account account = new Account();
User u_weChat = new User(account,);
User u_zhifubao = new User(account,);
Thread wechat = new Thread(u_weChat,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
wechat.start();
zhifubao.start();
}
}
class Account{
public static int money = ;
public void get4(int m,Account a) throws InterruptedException {
//表示通过方法的参数传递进来的对象的代码块加了同步锁
//不同的对象有不同的同步锁
synchronized (a) {
String thread = Thread.currentThread().getName();
//如果是微信操作,则暂时挂起,让支付宝先操作
if (thread.equals("微信")){
try {
a.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// System.out.println("正在运行:"+thread);
if(money<m) {
System.out.println(thread+"操作-账户金额不足:" + money);
}else {
System.out.println(thread+"操作-账户原有金额:"+money);
System.out.println(thread+"操作-取款金额:"+m);
money = money - m;
System.out.println(thread+"操作-取款后的余额:"+money);
}
if (thread.equals("支付宝")){
a.notify();
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
account.get4(money,account);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我们先让线程为“微信”的暂时挂起,先执行其它的,也就是支付宝,然后当支付宝执行完毕之后告知微信,这样就可以了。
输出:
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
微信操作-账户金额不足:1000
这时,我们又想到,假设我们现在传入的是不同的Account对象呢?
我们先看下输出结果:
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
程序一直在运行(这是我在notify if语句之前加的)
这说明了什么?支付宝运行结束后并没有成功告知微信,微信一直处于等待状态,其原因在于它们拥有的是不同对象的锁,因此之间不能通信。
java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)的更多相关文章
- Java:多线程概述与创建方式
目录 Java:多线程概述与创建方式 进程和线程 并发与并行 多线程的优势 线程的创建和启动 继承Thread类 start()和run() 实现Runnable接口 实现Callable接口 创建方 ...
- 【多线程】创建线程方式一:继承Thread类
创建线程方式一:继承Thread类 代码示例: /** * @Description 继承Thread类,重写run方法,调用start开启线程 * @Author hzx * @Date 2022- ...
- java多线程的两种创建方式
方式一:继承Thread类 1.创建一个继承于Thread类的子类 2.重写Thread类的run()方法---> 将此线程执行的操作声明在run()中 3.创建Thread类的子类的对象 4. ...
- 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。
返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...
- 多线程——Java中继承Thread类与实现Runnable接口的区别
线程我只写过继承Thread类的,后来知道java多线程有三种方式,今天首先比较一下常用的继承Thread类和实现Runnable接口的区别. 按着Ctrl键进入Thread之后,发现Thread类也 ...
- 多线程创建的方式一(继承Thread类)
1 package multithread; 2 3 /* 4 * 如何创建一个线程呢? 5 * 6 * 创建线程方式一:继承Thread类. 7 * 8 * 步骤: 9 * 1,定义一个类继承Thr ...
- Java线程的实现/创建方式
1.继承Thread类: Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例. 启动线程的唯一方法就是通过 Thread 类的 start()实例方法. start( ...
- [原创]java WEB学习笔记94:Hibernate学习之路---session 的管理,Session 对象的生命周期与本地线程绑定
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- day 33 什么是线程? 两种创建方式. 守护线程. 锁. 死锁现象. 递归锁. GIL锁
一.线程 1.进程:资源的分配单位 线程:cpu执行单位(实体) 2.线程的创建和销毁开销特别小 3.线程之间资源共享,共享的是同一个进程中的资源 4.线程之间不是隔离的 5.线程可不需 ...
随机推荐
- Rust 入门 (三)_上
这部分我们学习 rust 语言的 变量.数据类型.函数.注释.流程控制 这五个方面的内容.本文我们介绍前两个内容,下一篇文章介绍后三个内容. 变量 默认情况下,rust 语言中声明的变量是不可变的,比 ...
- html基础——表格练习
最终样式 步骤分析: 标题和报名时间为一块 表格为一块 由图可知,可创建一个七行八列的列表存储数据 首先设置边框的样式,边框 大小,这里是黑色不好看可以设置为天空蓝 可选矿使用<input ty ...
- nginx实现内网服务唯一端口外网映射
2.1 内网服务唯一端口外网映射 (一) 组网图 (二) 简要说明: 如标题所示,该功能可以实现内网环境下所有服务端口通过nginx的正向代理通过唯一端口映射至 ...
- 完全理解JS原型指南
目录 Table of Contents generated with DocToc 目录 一.参考书籍和数据 二.原型,[[prototype]]和.prototype以及constructor 三 ...
- Git实战指南----跟着haibiscuit学Git(第二篇)
笔名: haibiscuit 博客园: https://www.cnblogs.com/haibiscuit/ Git地址: https://github.com/haibiscuit?tab=re ...
- 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(2)- Boot配置(ISP_Pin, OTP)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的Boot配置. 在上一篇文章 Boot简介 里痞子衡为大家介绍了Boot基本原理以及i.MXRTx ...
- 【Android - IPC】之Binder机制简介
参考资料: 1.<Android开发艺术探索>第二章2.3.3 Binder 2.[Android Binder设计与实现-设计篇] 3.[Android Binder机制介绍] 1. 什 ...
- jQuery中带有异步Ajax代码的执行顺序
1.问题 在使用jQuery的时候想通过Ajax请求来放所需要的html代码中放值,但是请求回来却是空的,源代码如下: $("a").click(function() { // ...
- Thinkphp5.1 导入第三方包的问题
一般刚接触tp5.1的,会很不适应,虽然版本号只是比5.0多了0.1,但是差别挺大,废弃了不少方法,官方的教程又很简单,很多东西没说全,在此鄙视一下框架作者,最起码体谅一下小白嘛,搞了好多天才把5.1 ...
- Spring boot如何快速的配置多个Redis数据源
简介 redis 多数据源主要的运用场景是在需要使用多个redis服务器或者使用多个redis库,本文采用的是fastdep依赖集成框架,快速集成Redis多数据源并集成lettuce连接池,只需引入 ...