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{
    void run() {
        System.out.println("多线程运行的代码");
        for(int i=0;i<10;i++) {
            System.out.println("这是多线程的逻辑代码");
        }
    }
}
class Test {
    static  main(String[] args) {
        Thread t0 = new TestThread();
        //在开启了线程后,线程与main()方法并行运行
        t0.start();启动线程
    }
}

2.实现Runnable接口

  • 定义子类,实现Runnable接口
  • 子类中重写Runnable接口中的run方法
  • 通过Thread类含参构造器创建线程对象
  • 将Runanable接口的子类对象作为实际参数传递给Thread类的构造方法中
  • 调用Thread类的start方法:开启线程、调用Runnable子类接口的run方法
class TestRunnable implements Runnable{

    @Override
     run() {
         TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getName()+"开始运行");
        }
    }
    

}
new Thread( TestRunnable());
        t0.start();
        Thread t1 = 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;

 Test2 {
     main(String[] args) {
        Account account =  Account();
        User u_weChat = new User(account,2000);
        User u_zhifubao = );
        Thread wechat = new Thread(u_weChat,1)">"微信");
        Thread zhifubao = new Thread(u_zhifubao,1)">支付宝);
        wechat.start();
        zhifubao.start();
        
    }
}
 Account{
    int money = 3000;
    
    void get(int m) {
        String thread = Thread.currentThread().getName();
        System.out.println(正在运行:"+thread);
        if(money<m) {
            System.out.println(thread+操作-账户金额不足:" + money);
        }else {
            System.操作-账户原有金额:money);
            System.操作-取款金额:m);
            money = money - m;
            System.操作-取款后的余额:money);
        }
    }
}
 User implements Runnable{
    Account account;
     money;
    public User(Account account, money) {
        this.account = account;
        this.money = money;
    }
    
    @Override
     TODO Auto-generated method stub
        account.get();
    }
    
}

输出:

正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
微信操作-取款金额:2000
微信操作-取款后的余额:-1000

此时,正如以上我们所说的,取款不合法了,那么如何解决呢?可以给方法加上同步锁。但是,需要注意的是:

 Account();
        Account account1 =  Account();
//意思是这里创建了不同的Account对象,获得的锁是不同对象的锁 User u_weChat
= new User(account1,1)">在普通方法上加synchronized,锁的是整个对象,而不是某一个方法 不同的对象就是不同的锁 public synchronized void wGet(money); } } void zGet(money); } } } TODO Auto-generated method stub if(Thread.currentThread().getName().equals()) { account.wGet(money); } { account.zGet(money); } } }

输出:

正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
微信操作-取款金额:2000
支付宝操作-账户原有金额:3000
微信操作-取款后的余额:1000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:-1000

因为此时不同对象获得的是不同的锁,所以这种方式并不行,那么如何进行改动呢?只需要使用同一个对象即可,即:

        Account account = 2000);

此时输出:

正在运行:微信
微信操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
正在运行:支付宝
支付宝操作-账户金额不足:1000

同时,还可以进一步简化:

    money);
        }
    }

在最后调用时,只需要在run()方法中调用account.get()即可,不用将两个方法分开来写。

获得锁的线程会执行完毕后,才将锁交给下一个线程继续执行。

对于在静态方法上加锁:

 Account();
//意思是这里虽然创建了两个不同的Account对象,但是获得的锁是同一个锁 User u_weChat
= 静态方法加同步锁之后,对于所有的对象都是同一个锁 public static synchronized void get(money); } } } TODO Auto-generated method stub account.get(money); } }

对于这种方式,即使传入的是该类的不同对象,仍然获得的是同一个锁。

正在运行:微信
微信操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
正在运行:支付宝
支付宝操作-账户金额不足:1000

还有一种方式是利用同步锁修饰代码块:

void get2( m) {
//用this锁代码块是代表当前的对象,如果在其它方法中也有synchronized(this)的代码块用的是同一个同步锁 synchronized (
this) { String thread = Thread.currentThread().getName(); System.thread); m) { System. money); } { System.money); System.m); money = money - m; System.money); } } } } account.get2(money); } }

输出:

正在运行:支付宝
支付宝操作-账户原有金额:3000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:1000
正在运行:微信
微信操作-账户金额不足:1000

想要不同的对象有不同的锁:

;

    void get3( m,Account a) {
        表示通过方法的参数传递进来的对象的代码块加了同步锁
        不同的对象有不同的同步锁
        synchronized (a) {
            String thread =        account.get3(money,account);
    }
    
}

输出:

正在运行:微信
正在运行:支付宝
微信操作-账户原有金额:3000
支付宝操作-账户原有金额:3000
微信操作-取款金额:2000
微信操作-取款后的余额:1000
支付宝操作-取款金额:2000
支付宝操作-取款后的余额:-1000

此时,这种加锁方式就没有效果了,与最开始的那种是一样的。

总而言之,要想使加锁有效果,必须获得的是同一个对象的锁。

五、线程之间的通信

wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。

notify():唤醒正在排队等待同步资源的线程中优先级较高者结束等待。

notifyAll():唤醒正在排队等待资源的所有线程结束等待。

java.lang.Object提供的三个方法只有在sychronized方法或synchronized代码块中才能使用。

对于第四节提到到最后一种方式,只要将使用同一个Account对象,加锁机制就还是成功。但是我们会发现,之前的结果都是微信在支付宝之前进行操作假设我们要让支付宝先操作,又应该怎么办呢?这时就需要利用线程之间的通信。

void get4( Thread.currentThread().getName();
            如果是微信操作,则暂时挂起,让支付宝先操作
            if (thread.equals("微信")){
                try {
                    a.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("正在运行:"+thread);
            money);
            }
            if (thread.equals("支付宝")){
                a.notify();
            }
        }
    }
}
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语句之前加的)

这说明了什么?支付宝运行结束后并没有成功告知微信,微信一直处于等待状态,其原因在于它们拥有的是不同对象的锁,因此之间不能通信。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越