美文网首页
可能是东半球最好的多线程讲义!

可能是东半球最好的多线程讲义!

作者: 熵增学院 | 来源:发表于2018-08-07 10:38 被阅读0次

JAVA多线程

多线程的基本概念

线程指进程中的一个执行场景,也就是执行流程,那么进程和线程有什么区别呢?

• 每个进程是一个应用程序,都有独立的内存空间

• 同一个进程中的线程共享其进程中的内存和资源(共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的。)

什么是进程?

一个进程对应一个应用程序。例如:在windows操作系统启动Word就表示启动了一个

进程。在java的开发环境下启动JVM,就表示启动了一个进程。现代的计算机都是支持多

进程的,在同一个操作系统中,可以同时启动多个进程。

多进程有什么作用?

单进程计算机只能做一件事情。

玩电脑,一边玩游戏(游戏进程)一边听音乐(音乐进程)。

对于单核计算机来讲,在同一个时间点上,游戏进程和音乐进程是同时在运行吗?不是。

因为计算机的CPU只能在某个时间点上做一件事。由于计算机将在“游戏进程”和“音乐

进程”之间频繁的切换执行,切换速度极高,人类感觉游戏和音乐在同时进行。

多进程的作用不是提高执行速度,而是提高CPU的使用率。

进程和进程之间的内存是独立的。

什么是线程?

线程是一个进程中的执行场景。一个进程可以启动多个线程。

多线程有什么作用?

多线程不是为了提高执行速度,而是提高应用程序的使用率。

线程和线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈。

可以给现实世界中的人类一种错觉:感觉多个线程在同时并发执行。

java程序的运行原理?

java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法。所以main方法运行在主线程中。在此之前的所有程序都是单线程的。

线程生命周期

线程是一个进程中的执行场景,一个进程可以启动多个线程


1 (2).png

新建:采用new语句创建完成

就绪:执行start后

运行:占用CPU时间

阻塞:执行了wait语句、执行了sleep语句和等待某个对象锁,等待输入的场合

终止:退出run()方法

多线程不是为了提高执行速度,而是提高应用程序的使用率.

线程和线程共享”堆内存和方法区内存”.栈内存是独立的,一个线程一个栈.

可以给现实世界中的人类一种错觉:感觉多线程在同时并发执行.

很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。

• 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程

• 并行与并发:

o 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

o 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:

voidtransferMoney(Userfrom,Userto,floatamount){

to.setMoney(to.getBalance()+amount);

from.setMoney(from.getBalance()-amount);

}

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程,该进程会自动启动一个”主线程”,

然后主线程去调用某个类的main()方法,所以main()方法运行在主线程中.


2.png

线程的调度与控制

线程的调度模型分为:分时调度模型和抢占式调度模型,Java使用抢占式调度模型

通常我们的计算机只有一个CPU,CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。在单CPU的机器上线程不是并行运行的,只有在多个CPU上线程才可以并行运行。Java虚拟机要负责线程的调度,取得CPU的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,Java使用抢占式调度模型。分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。

 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片

 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.

publicclassThreadTest{

publicstaticvoidmain(String[]args){

ThreadTest1();

//ThreadTest2();

//ThreadTest3();

//ThreadTest4();

//ThreadTest5();

}

/**

*三个方法:获取当前线程对象:Thread.currentThread();给线程起名:t1.setName("t1");获取线程的名字:

*t.getName();

*/

privatestaticvoidThreadTest1(){

Threadt=Thread.currentThread();//t保存的内存地址指向的线程为"主线程"

System.out.println(t.getId());

Threadt1=newThread(newProcessor1());

//给线程起名

t1.setName("t1");

t1.start();

Threadt2=newThread(newProcessor1());

t2.setName("t2");

t2.start();

}

/**

*线程优先级高的获取的CPU时间片相对多一些优先级:1-10最低:1最高:10默认:5

*/

privatestaticvoidThreadTest2(){

Threadt1=newProcessor2();

Threadt2=newProcessor2();

t1.setName("t1");

t2.setName("t2");

System.out.println(t1.getPriority());

System.out.println(t2.getPriority());

t1.setPriority(1);

t2.setPriority(10);

t1.start();

t2.start();

}

/**

*1.Thread.sleep(毫秒);2.sleep方法是一个静态方法3.该方法的作用:阻塞当前线程,腾出CPU,让给其它线程

*/

privatestaticvoidThreadTest3(){

Threadt=newThread(newProcessor3());

t.start();

for(inti=0;i<11;i++){

System.out.println(Thread.currentThread().getName()+"========>"

+i);

try{

t.sleep(5000);//等同于Thread.sleep(5000);阻塞的还是当前线程,和t线程无关.

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

/**

*某线程正在休眠,如何打断它的休眠以下方式依靠的是异常处理机制

*/

privatestaticvoidThreadTest4(){

try{

Threadt=newThread(newProcessor4());

t.start();

Thread.sleep(5000);//睡5s

t.interrupt();//打断Thread的睡眠

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

/**

*如何正确的更好的终止一个正在执行的线程需求:线程启动5s之后终止.

*/

privatestaticvoidThreadTest5(){

Processor5p=newProcessor5();

Threadt=newThread(p);

t.start();

//5s之后终止

try{

Thread.sleep(5000);

p.isRun=false;

}catch(InterruptedExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}

}

}

classProcessor1implementsRunnable{

@Override

publicvoidrun(){

Threadt=Thread.currentThread();//t保存的内存地址指向的线程为"t1线程对象"

System.out.println(t.getName());

System.out.println(t.getId());

}

}

classProcessor2extendsThread{

@Override

publicvoidrun(){

for(inti=0;i<50;i++){

System.out.println(Thread.currentThread().getName()

+"----------->"+i);

}

}

}

classProcessor3implementsRunnable{

/**

*Thread中的run方法不能抛出异常,所以重写runn方法之后,在run方法的声明位置上不能使用throws

*所以run方法中的异常只能try...catch...

*/

@Override

publicvoidrun(){

for(inti=0;i<11;i++){

System.out.println(Thread.currentThread().getName()+"========>"

+i);

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}

classProcessor4implementsRunnable{

@Override

publicvoidrun(){

try{

Thread.sleep(1000000000);

System.out.println("能否执行这里");

}catch(InterruptedExceptione){

e.printStackTrace();

}

for(inti=0;i<11;i++){

System.out.println(Thread.currentThread().getName()+"========>"

+i);

}

}

}

classProcessor5implementsRunnable{

booleanisRun=true;

@Override

publicvoidrun(){

for(inti=0;i<11;i++){

if(isRun){

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

+"========>"+i);

}

}

}

}

线程优先级

线程优先级主要分三种:MAX_PRIORITY(最高);MIN_PRIORITY(最低级)NORM_PRIORITY(标准)默认

//设置线程的优先级,线程启动后不能再次设置优先级

//必须在启动前设置优先级

//设置最高优先级

t1.setPriority(Thread.MAX_PRIORITY);

sleep

publicclassSleepTest{

publicstaticvoidmain(String[]args){

System.out.println("Wait");

//让主线程等待5秒再执行

Wait.bySec(5);

//提示恢复执行

System.out.println("start");

}

}

classWait{

publicstaticvoidbySec(longs){

//sleeps个1秒

for(inti=0;i<s;i++){

System.out.println(i+1+"秒");

try{

//sleep1秒

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}

停止一个线程

 如果我们的线程正在睡眠,可以采用interrupt进行中断

 通常定义一个标记,来判断标记的状态停止线程的执行

yield

它与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会,采用yieid可以将CPU的使用权让给同一个优先级的线程

publicclassYieldTest{

publicstaticvoidmain(String[]args){

FirstThreadmt=newFirstThread();

SecThreadmnt=newSecThread();

mt.start();

mnt.start();

}

}

classFirstThreadextendsThread{

publicvoidrun(){

for(inti=0;i<5;i++){

System.out.println("第一个线程的第"+(i+1)+"次运行");

Thread.yield();//暂停线程

}

}

}

classSecThreadextendsThread{

publicvoidrun(){

for(inti=0;i<5;i++){

System.out.println("第二个线程的第"+(i+1)+"次运行");

Thread.yield();

}

}

}

join

当前线程可以调用另一个线程的join方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行

publicclassJoinTestextendsThread{

publicJoinTest(Stringname){

super(name);

}

publicvoidrun(){

for(inti=0;i<5;i++)

System.out.println(getName()+""+i);

}

publicstaticvoidmain(String[]args){

for(inti=0;i<10;i++){

if(i==5){

JoinTesttempjt=newJoinTest("半路加入的线程");

try{

tempjt.start();

tempjt.join();

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName()+""+i);

}

}

}

synchronized

线程同步,指某一个时刻,指允许一个线程来访问共享资源,线程同步其实是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执行一个方法,采用线程同步解决以上的问题,我们只要保证线程一操作s时,线程2不允许操作即可,只有线程一使用完成s后,再让线程二来使用s变量

• 异步编程模型:t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁.

• 同步编程模型:t1线程和t2线程执行,t2线程必须等t1线程执行结束之后,t2线程才能执行,这是同步编程模型.

• 什么时候要用同步呢?为什么要引入线程同步呢?

• 1.为了数据的安全,尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制.

• 线程同步机制使程序变成了(等同)单线程.

• 2.什么条件下要使用线程同步?

• 第一:必须是多线程环境

• 第二:多线程环境共享同一个数据.

• 第三:共享的数据涉及到修改操作.

//synchronized是对对象加锁

//采用synchronized同步最好只同步有线程安全的代码

//可以优先考虑使用synchronized同步块

//因为同步的代码越多,执行的时间就会越长,其他线程等待的时间就会越长

//影响效率

publicclassTestWithdrawal{

publicstaticvoidmain(String[]args){

//创建两个线程

TestAccountr=newTestAccount();

Threadone=newThread(r);

Threadtwo=newThread(r);

one.setName("张三");

two.setName("张三的妻子");

//启动线程

one.start();

two.start();

}

}

classAccount{

privateintbalance=500;//余额

publicintgetBalance(){

returnbalance;

}

//取款

publicvoidwithdraw(intamount){

balance=balance-amount;

}

}

classTestAccountimplementsRunnable{

//所有用TestAccount对象创建的线程共享同一个帐户对象

privateAccountacct=newAccount();

publicvoidrun(){

for(inti=0;i<5;i++){

makeWithdrawal(100);//取款

if(acct.getBalance()<0){

System.out.println("账户透支了!");

}

}

}

privatevoidmakeWithdrawal(intamt){

synchronized(acct){

if(acct.getBalance()>=amt){

System.out.println(Thread.currentThread().getName()+"准备取款");

try{

Thread.sleep(500);//0.5秒后实现取款

}catch(InterruptedExceptionex){

}

//如果余额足够,则取款

acct.withdraw(amt);

System.out.println(Thread.currentThread().getName()+"完成取款,余额:"+acct.getBalance());

}else{

//余额不足给出提示

System.out.println("余额不足以支付"

+Thread.currentThread().getName()+"的取款,余额为"

+acct.getBalance());

}

}

}

}

死锁

publicclassDeadLock{

publicstaticvoidmain(String[]args){

Objecto1=newObject();

Objecto2=newObject();

Threadt1=newThread(newT1(o1,o2));

Threadt2=newThread(newT2(o1,o2));

t1.start();

t2.start();

}

}

classT1implementsRunnable{

Objecto1;

Objecto2;

T1(Objecto1,Objecto2){

this.o1=o1;

this.o2=o2;

}

@Override

publicvoidrun(){

synchronized(o1){

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

synchronized(o2){

}

}

}

}

classT2implementsRunnable{

Objecto1;

Objecto2;

T2(Objecto1,Objecto2){

this.o1=o1;

this.o2=o2;

}

@Override

publicvoidrun(){

synchronized(o2){

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

synchronized(o1){

}

}

}

}

守护线程

从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如java中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。

• 其它所有的用户线程结束,则守护线程退出!

• 守护线程一般都是无限执行的.

publicclassDaemonThread{

publicstaticvoidmain(String[]args)throwsInterruptedException{

Threadt1=newThread(newRunnable2());

t1.setName("t1");

//将t1这个用户线程修改成守护线程.在线程没有启动时可以修改以下参数

t1.setDaemon(true);

t1.start();

//主线程

for(inti=0;i<10;i++){

System.out.println(Thread.currentThread().getName()+"----->"+i);

Thread.sleep(1000);

}

}

}

classRunnable2implementsRunnable{

@Override

publicvoidrun(){

inti=0;

while(true){

i++;

System.out.println(Thread.currentThread().getName()+"-------->"

+i);

try{

Thread.sleep(500);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}

设置为守护线程后,当主线程结束后,守护线程并没有把所有的数据输出完就结束了,也即是说守护线程是为用户线程服务的,当用户线程全部结束,守护线程会自动结束

Timer.schedule()

/**

*关于定时器的应用作用:每隔一段固定的时间执行一段代码

*/

publicclassTimerTest{

publicstaticvoidmain(String[]args)throwsParseException{

//1.创建定时器

Timert=newTimer();

//2.指定定时任务

t.schedule(newLogTimerTask(),newSimpleDateFormat(

"yyyy-MM-ddHH:mm:ssSSS").parse("2017-06-2914:24:00000"),

10*1000);

}

}

//指定任务

classLogTimerTaskextendsTimerTask{

@Override

publicvoidrun(){

System.out.println(newSimpleDateFormat("yyyy-MM-ddHH:mm:ssSSS")

.format(newDate()));

}

}

我是melon,一个10年编程老司机。Q我3474203856。 给melon留言或者说明是看到文章过来的。所有相关技术问题都可以一起寻找到答案。

相关文章

  • 可能是东半球最好的多线程讲义!

    JAVA多线程 多线程的基本概念 线程指进程中的一个执行场景,也就是执行流程,那么进程和线程有什么区别呢? • 每...

  • iBooker 布客老实人报 vol4

    看看我们的帽子,这就是老实人的力量! 01 罗永浩即将发布新产品:可能是东半球最好的充气娃娃 02 女装、退学、离...

  • 常用工具类

    1,下载工具 FileDownloader EasyRecyclerView 2.可能是东半球最全的RxJava使...

  • Android主流框架学习资料

    Android 开发小工具之:注解 Annotation 可能是东半球最全的RxJava使用场景小结 Androi...

  • 先测试一下

    据说是东半球最好用的写作工具,我希望东半球实际上存在的写作工具是超过十个的。 这是一个一级标题 然后用回车,我发现...

  • 试试有多好用

    听说你很好用,号称东半球最好用的写字软件,Mark一下,今天一试看效果。

  • Android - 使用HttpURLConnection实现多

    使用HttpURLConnection实现多线程下载 这个小程序是根据《疯狂Android讲义》(第三版)第13章...

  • Cloudopt-让安全成为习惯

    Cloudopt可能是东半球最强安全插件,能够实时保护您的安全、防止追迹、恶意域名,过滤横幅广告、弹窗广告以及视频...

  • Win32多线程程序设计读书笔记

    为什么多线程? 多线程并不一定是最好的,合适才是最好的。 多线程主要的优点是价廉物美,启动快、退出快、与其他线程共...

  • 希望是最全的RxJava2使用场景小结

    因为之前是看可能是东半球最全的RxJava使用场景小结来学习RxJava的,由于升级2.0版本部分API已经替换。...

网友评论

      本文标题:可能是东半球最好的多线程讲义!

      本文链接:https://www.haomeiwen.com/subject/ibjovftx.html