多线程

概念:

进程--是一个正在执行中的程序。

  每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做一个控制单元。

线程--就是进程中的一个独立的控制单元。

  线程在控制着进程的执行。


Java虚拟机启动的时候会有一个进程java.exe, 该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,改线程称之为主线程。

扩展:其实更细节说明java Virtual Machine, JVM启动不止一个线程,还有负责垃圾回收机制的线程。


1,如何在自定义的代码中,自定义一个线程呢?

方法1:

继承Thread类

步骤:

1 定义类继承Thread

2 复写Thread类中的run方法。====》目的:将自定义的代码存储在run方法,让线程运行。

3 调用线程的start方法,该方法有两个作用:开始线程+调用run方法

eg:两个类一个运行主线程main,一个运行子线程:

public class Demoextends Thread {


@Override

public void run() {

super.run();

for (int i = 0; i < 60; i++)

System.out.println("demo run" + i);


}


}



public class ThreadDemo {

public static void main(String[] args) {

Demo d = new Demo();

d.start();


for (int i = 0; i < 60; i++)

System.out.println("Hello World!" + i);

}

}





发现运行结果每一次都不同,因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。

明确一点:在某一时刻只能有一个程序在运行,(多核除外)

CPU在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象把多线程的运行行为看作是在互相抢夺CPU的执行权。

这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长时间,CPU说了算。


为什么要创建run方法?

Thread类用于描述线程。

该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。

也就是说Thread类中的run方法,用于存储线程要运行的代码。


如果不调用start(),即说明线程执行没有开始。。。。。。




多线程的运行状态:



线程拥有自己默认的名称。Thread-编号 该编号从0开始。


static Thread currentThread();获取当前线程对象。

getName();获取线程名称。


设置线程的名称,setName()或者构造函数。


如何让 几个线程共享一个变量?

将这个变量设为static


方法二: 实现Runnable接口


步骤:

1 定义类实现Runnable接口

2 覆盖Runnable接口中的run方法

将线程要运行的代码存放在该run方法中

3 通过Thread类简历线程对象

4 将Runnable 接口的子类对象作为实际参数传递给Thread类的构造函数

为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为,自定义的run方法所属的对象是Runnable接口的子类对象,所以,要让线程去制定制定对象的run方法,就必须明确该run方法所属对象。

5 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法


实现方式和继承方式有什么区别?

实现方式好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。eg:Student类继承Person类,但是Student类中部分代码是要用到多线程,,,由于智能单继承,所以智能实现runnable().


两种方式区别:

继承Thread:线程代码存放Thread子类run方法中。不共享非静态变量

实现Runnable:存成代码存放在接口的子类的run方法中。 共享静态变量



多线程运行时,存在安全隐患,eg:只打印ticket>0的值,但是可能会打印出小于0的值。(当加入Thread.sleep(100)来模拟多线程的等待现象)。

问题原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。=======同步代码块:

synchronized(对象){

需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行。没有锁的线程即使获得cpu的执行权,也进不去,因为没有获取锁。

火车上的卫生间-----经典例子:

每个人都想往里冲,但是,一次只能一个人进入,进入后将门锁上,之后被熏晕,即使此时坑没有被占,。。。

同步的前提:

1 必须要有两个或者两个以上的线程

2 必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,会消耗更多的资源。


举例:

银行有一个金库,有两个储户分别存300rmb,每次存100,存三次。

目的:该程序是否有安全问题,如果有,如何解决?

如何找问题:

1 明确哪些代码是多线程运行代码

2 明确共享数据

3 明确多线程运行代码中哪些语句是操作共享数据的。


两种同步方式:

1 同步代码块

Object obj = new Object();

public void run( ){

while(true){

synchronized(obj){

Try(Thread.sleep(10);) catch(Exception e){}

System.out.println(ticket--);

}

}

}

2 同步函数(将共享数据的代码抽离成一个函数)

public synchronized void show(){ 

Try(Thread.sleep(10);) catch(Exception e){}

System.out.println(ticket--);

}


同步函数用的是哪一个锁?

函数需要被对象调用,那么函数都有一个所需对象引用,就是this.

所以同步函数使用的锁是this.


如果想让同步代码块,和同步函数共享数据,且保证安全性?

同步代码块,synchronized(this){....}, 因为要想共享数据,得让他们的锁保持一致。


多线程-静态同步函数的锁是Class对象。

如果同步函数被静态修饰后,使用的锁是什么呢?

由于静态方法中不能定义this,所以静态同步函数的锁不是this.

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。====类名.class, 该对象的类型是Class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象,即:类名.class

所以:

静态同步函数:

public static synchronized void show(){

if(ticket>0){

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

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

}

}

静态同步代码块:

synchronized(Ticket.class){

if(ticket>0){

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

System.......

}

}



死锁:同步中嵌套同步,






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值