多线程

作者: yangliangliang | 来源:发表于2017-06-15 15:02 被阅读12次

进程:当一个程序进入内存运行,即变成一个进程

(单核计算机在同一个时间点上,游戏进程和音乐进程是同时在运行吗?不是,因为计算机的CPU只能在某个时间点上做一件事。由于计算机将在游戏进程和音乐进程之间频繁的执行,切换速度极高,人类感觉游戏和音乐在同时进行。(一个大脑)多进程并发其实是2核和4核的计算机,(2个和4个大脑)

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

多线程不是为了提高执行速度,而是提高程序的使用率。可以给现实世界中的人类一种错觉,感觉多个线程在同时并发执行。

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

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

线程线程是进程中的一个执行单元。负责当前进程中程序的执行。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

多线程:即一个程序中有多个线程在同时执行。

举例:单线程程序:即去网吧,一个人下机下一人上机。多线程程序:网吧能够让多个人同时上网。

java程序的运行原理?

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

程序运行原理:

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

抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的为抢占式调度。(正确理解为优先级高的线程获取的CPU时间片多一点)

抢占式调度详解:QQ,迅雷同时在运行,感觉这些软件好像在同一时刻运行着。实际上,CPU(中央处理器)使用抢占式模式在多个线程间进行着高速的切换,对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率。让CPU的使用率更高。

在java语言中实现多线程的第一种方式:

第一步:继承java.lang.Thread;

第二步:重写run方法.

三个知识点:

如何定义线程?

如何创建线程?

如何启动线程?

*/

public class ThreadTest02

{

public static void main(String[] args){

//创建线程

Thread t = new Processor();

//启动

t.start(); //这段代码执行瞬间结束。告诉JVM再分配一个新的栈给t线程.

//run不需要程序员手动调用,系统线程启动之后自动调用run方法.

//t.run(); //这是普通方法调用,这样做程序只有一个线程,run方法结束之后,下面程序才能继续执行。

//这段代码在主线程中运行.

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

System.out.println("main-->" + i);

}

//有了多线程之后,main方法结束只是主线程栈中没有方法栈帧了。

//但是其他线程或者其他栈中还有栈帧。

//main方法结束,程序可能还在运行。

}

}

//定义一个线程

class Processor extends Thread

{

//重写run方法

public void run(){

for(int i=0;i<30;i++){

System.out.println("run-->" + i);

}

}

}

java中实现线程的第二种方式:

第一步:写一个类实现java.lang.Runnable;接口

第二步:实现run方法.

public class ThreadTest03

{

public static void main(String[] args){

//创建线程

Thread(Runnable target)构造方法

Thread t =new Thread(Runable);创建线程其实底层是这个方法

Thread t = new Thread(new Processor());

//启动

t.start();

}

}

//这种方式是推荐的。因为一个类实现接口之外保留了类的继承。

class Processor implements Runnable

{

public void run(){

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

System.out.println("run-->"+i);

}

}

}

线程的生命周期?

线程生命周期结构图

线程新建之后调用start方法进入就绪状态,就绪状态的线程表示有权力去获取CPU的时间片,拿到CPU的时间片进入运行状态,运行执行run方法,时间片用完后再回去到就绪状态,等待拿到CPU时间片,拿到CPU时间片再去运行run方法,反复如此,直到run方法执行完毕,这个线程就消亡。运行时候会出现线程阻塞,线程阻塞后等待线程解除阻塞,解除阻塞后,进入就绪状态

CPU时间片:是执行权,当线程拿到CPU时间片之后就马上执行run方法,这个时候就带白哦进入了运行状态。

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

就绪:执行start后

运行:占用CPU时间

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

终止:退出run()方法

2.线程的调度与控制

通常我们的计算机只有一个CPU,CPU在某一个时刻职能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行命令,在单CPU的机器上线程不是并行运行的,只有在多个CPU上线程才可以并行运行,java虚拟机要负责线程的调度,取得CPU的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,java使用抢占式的调度模型。

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

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

2.1.线程的优先级

线程的优先级主要分三种:max_priority(最高级10); min_priority(最低级1) norm_priority(标准5)默认(优先级一般是1--10)

1.获取当前线程对象Thread.currentThread();

2.给线程起名 t.setName("t1");

3.获取线程的名字 t.getName();

线程优先级高的获取的CPU时间片相对多一些。

优先级:1-10。//设置优先级   t1.setPriority(5);

System.out.println(Thread.NORM_PRIORITY); //5

1.Thread.sleep(毫秒);

2.sleep方法是一个静态方法.

3.该方法的作用:阻塞当前线程.腾出CPU,让给其他线程。

public class ThreadTest06

{

public static void main(String[] args) throws InterruptedException{

//Thread.currentThread(); //t保存的内存地址指向的线程是“主线程对象”

Thread t1 = new Processor();

t1.setName("t1");

t1.start();

//阻塞主线程

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

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

Thread.sleep(500);

}

}

}

class Processor extends Thread

{

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

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

public void run(){

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

//Thread.currentThread(); //t保存的内存地址指向的线程是“t1线程对象”

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

try{

Thread.sleep(1000); //让当前线程阻塞1S。

}catch(InterruptedException e){

e.printStackTrace();

}

}

// m1();

}

//m1方法是可以使用throws的.

public void m1() throws Exception{

}

面试题:考点其实是对象也能调用静态方法()

//静态的方法按照正规的方式访问:“类名.”  //静态的方法也能用“引用.”访问  st.m2(); //编译阶段检查出st是StaticTest03类型,编译通过,运行的时候,仍然使用 "StaticTest03."的方式访问。//该方法执行不需要对象。//空的引用去访问成员的时候会出现空指针异常。//m2方法不是“成员”而是静态的。//所以即使引用是空的,也不会报空指针异常。(其实也就是说,m2 方法执行底层是 不需要这个对象的。)

public class ThreadTest07{

public static void main(String[] args) throws Exception{

//创建线程

Thread t = new Processor();

t.setName("t");

//启动线程

t.start();

//休眠

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

System.out.println("HelloWorld!");

A a = null;

a.m1(); //不会出现空指针异常。}

}

class Processor extends Thread

{

public void run(){

for(int i=0;i<200;i++){

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

} } }

class A{

public static void m1(){} }

yield 让位,它与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

某线程正在休眠,如果打断它的休眠.

以下方式依靠的是异常处理机制。//打断t的休眠.t.interrupt();

3.线程同步锁机制

t1和t2

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

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

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

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

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

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

第一:必须是多线程环境

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

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

以下程序演示取款例子。以下程序不使用线程同步机制,多线程

同时对同一个账户进行取款操作,会出现什么问题?

使用线程同步机制保证数据的安全。

public class ThreadTest12

{

public static void main(String[] args){

//创建一个公共的账户

Account act = new Account("actno-001",5000.0);

//创建线程对同一个账户取款

Thread t1 = new Thread(new Processor(act));

Thread t2 = new Thread(new Processor(act));

t1.start();

t2.start();

}}

//取款线程

class Processor implements Runnable

{

//账户

Account act; //成员变量账户

//Constructor提供构造方法

Processor(Account act){

this.act = act;

}

public void run(){

act.withdraw(1000.0);

System.out.println("取款1000.0成功,余额:" + act.getBalance());

}

}

//账户

class Account

{

private String actno; // 账户

private double balance; 余额

public Account(){}

public Account(String actno,double balance){

this.actno = actno;

this.balance = balance;

}

//setter and getter

public void setActno(String actno){

this.actno = actno;

}

public void setBalance(double balance){

this.balance = balance;

}

public String getActno(){

return actno;

}

public double getBalance(){

return balance;

}

//对外提供一个取款的方法

public void withdraw(double money){ //对当前账户进行取款操作

double after = balance - money;

//延迟

try{Thread.sleep(1000);}catch(Exception e){}

//更新

this.setBalance(after);

}

}

//对外提供一个取款的方法

public void withdraw(double money){ //对当前账户进行取款操作

原理:t1线程和t2线程.

t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,

如果找到this对象锁,则进入同步语句块中执行程序。当同步语句块中的代码

执行结束之后,t1线程归还this的对象锁。

在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到

synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程持有,

只能在这等待this对象的归还。

//把需要同步的代码,放到同步语句块中. this就是共享对象this就是账户

synchronized(this){

double after = balance - money;

//延迟

try{Thread.sleep(1000);}catch(Exception e){}

//更新

this.setBalance(after);

}}}

总结:线程执行代码,遇到了synchronized关键字,就会去这个对象的对象锁,找到则执行,找不到则等待。再看它是不是共享同一个数据

//synchronized关键字添加到成员方法上,线程拿走的也是this的对象锁。

public synchronized void withdraw(double money){ //对当前账户进行取款操作

double after = balance - money;

//延迟

try{Thread.sleep(1000);}catch(Exception e){}

//更新

this.setBalance(after);

}

}

这样的使用方式也可以,比第一种范围大了。根据需求使用


相关文章

  • iOS多线程 NSOperation

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程 pthread、NSThread

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程: GCD

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程运用

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程基础

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • 多线程介绍

    一、进程与线程 进程介绍 线程介绍 线程的串行 二、多线程 多线程介绍 多线程原理 多线程的优缺点 多线程优点: ...

  • iOS进阶之多线程管理(GCD、RunLoop、pthread、

    深入理解RunLoopiOS多线程--彻底学会多线程之『GCD』iOS多线程--彻底学会多线程之『pthread、...

  • iOS多线程相关面试题

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • 多线程之--NSOperation

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • iOS多线程之--NSThread

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

网友评论

      本文标题:多线程

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