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类、线程的同步、线程的生命周期、线程之间的通信)的更多相关文章

  1. Java:多线程概述与创建方式

    目录 Java:多线程概述与创建方式 进程和线程 并发与并行 多线程的优势 线程的创建和启动 继承Thread类 start()和run() 实现Runnable接口 实现Callable接口 创建方 ...

  2. 【多线程】创建线程方式一:继承Thread类

    创建线程方式一:继承Thread类 代码示例: /** * @Description 继承Thread类,重写run方法,调用start开启线程 * @Author hzx * @Date 2022- ...

  3. java多线程的两种创建方式

    方式一:继承Thread类 1.创建一个继承于Thread类的子类 2.重写Thread类的run()方法---> 将此线程执行的操作声明在run()中 3.创建Thread类的子类的对象 4. ...

  4. 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...

  5. 多线程——Java中继承Thread类与实现Runnable接口的区别

    线程我只写过继承Thread类的,后来知道java多线程有三种方式,今天首先比较一下常用的继承Thread类和实现Runnable接口的区别. 按着Ctrl键进入Thread之后,发现Thread类也 ...

  6. 多线程创建的方式一(继承Thread类)

    1 package multithread; 2 3 /* 4 * 如何创建一个线程呢? 5 * 6 * 创建线程方式一:继承Thread类. 7 * 8 * 步骤: 9 * 1,定义一个类继承Thr ...

  7. Java线程的实现/创建方式

    1.继承Thread类: Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例. 启动线程的唯一方法就是通过 Thread 类的 start()实例方法. start( ...

  8. [原创]java WEB学习笔记94:Hibernate学习之路---session 的管理,Session 对象的生命周期与本地线程绑定

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. day 33 什么是线程? 两种创建方式. 守护线程. 锁. 死锁现象. 递归锁. GIL锁

    一.线程     1.进程:资源的分配单位    线程:cpu执行单位(实体) 2.线程的创建和销毁开销特别小 3.线程之间资源共享,共享的是同一个进程中的资源 4.线程之间不是隔离的 5.线程可不需 ...

随机推荐

  1. Linux安装redis数据库

    这几天在搞redis数据库,花了好大功夫,才成功安装在Linux上,这里将自己的安装步骤分享出来,同时也做个记录,备忘. 新人一枚,不对之处,请多指教! 首先登陆Linux服务器 Linux里,我习惯 ...

  2. JavaScript笔记三

    1.数据类型 - JS中一共分成六种数据类型 - String 字符串 - Number 数值 - Boolean 布尔值 - Null 空值 - Undefined 未定义 - Object 对象 ...

  3. JavaScript基础目录

    一.JavaScript简介 1.JavaScript用途 2.JavaScript的诞生 3.JavaScript从丑小鸭到金凤凰 4.JavaScript非常好学 5.学习方法 二.Hello W ...

  4. 二叉查找树的平衡(DSW算法)

    树适合于表示某些领域的层次结构(比如Linux的文件目录结构),使用树进行查找比使用链表快的多,理想情况下树的查找复杂度O(log(N)),而链表为O(N),但理想情况指的是什么情况呢?一般指树是完全 ...

  5. Spring 框架基础(05):事务管理机制,和实现方式

    本文源码:GitHub·点这里 || GitEE·点这里 一.Spring事务管理 1.基础描述 Spring事务管理的本质就是封装了数据库对事务支持的操作,使用JDBC的事务管理机制,就是利用jav ...

  6. adb查看Android app包名

    1. 确保电脑已经安装了adb.exe文件 2. 使用USB连接你的手机到电脑上,输入下图所示的命令后按下回车键:adb devices 3. 使用手机打开某个应用程序 4.回到命令号窗口,输入代码如 ...

  7. Vue项目解析

    各个文件夹 node_modules:用来放环境依赖 public:用来放公共资源,里面的index.html文件,就是初始的挂载点.被app.vue给取代了. src:放各种资源的. assets: ...

  8. Linux发展历史(简略)

    LINUX UNIX历史发展 1969肯 汤姆森在DEC PDP-7机器上开发出了UNIX系统 1971肯 汤姆森的同事丹尼斯 里奇发明了C语言 1973UNIX系统绝大部分用C语言重写,为提高UNI ...

  9. 大白话OSI七层协议

    目录 一.物理层 二.数据链路层 2.1 以太网协议 2.2 Mac地址 2.3 广播地址 三.网络层 3.1 IP协议详解 3.1.1 IP地址的两部分 3.2 子网掩码详解 3.3 IP数据包详解 ...

  10. php 设置临时内存和超时设置脚本最大执行时间

    ini_set('memory_limit','3072M'); // 临时设置最大内存占用为3G set_time_limit(0); // 设置脚本最大执行时间 为0 永不过期