多线程,同步,锁

来源:互联网 发布:俯瞰风景 知乎 编辑:程序博客网 时间:2024/06/04 18:57


 (有几张图片穿不上来,复习去了)

多线程

进程:正在正在执行的程序,就像桌面上的程序一样cup在不断的切换,

而线程就是进程中的内容,每一个应用程序中都至少有一个线程,因为线程是程序中的控制单元或者叫执行路径。

 

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

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

线程:就是进程中一个独立的控制单元,

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

一个进程中至少有一个线程(控制单元)。

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

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

通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类

class Demo extends Thread

{

       public void run()

       {

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

       }

}

class ThreadDemo

{

       public static void main(String[] args)

       {

              Demo d = new Demo();//创建好一个线程。

              d.start();

       }

}

1创建线程的第一种方式,继承Thread类

步骤 1定义类继承Thread。

        2覆写thread中的run()方法

              目的:将自定义的代码存储在run方法中,让线程运行。

        3调用线程的Start方法,该方法有两个作用

                                                 1启动线程2调用run方法

 

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

,明确一点,在某一时刻,只能有一个程序在运行,(多核除外),cpu在做着快速切换,以达到看上去同时运行的效果。

 

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

这就是多线程的一个特性,随机性。谁抢到,谁执行,至于执行多长时间,cpu说的算。它在优化资源。

接下来,为什么要覆盖run()方法呢?你不覆盖,他要运行什么呢:

Thread类用于描述线程,为什么要用线程呢,就是因为我们要用线程同时运行多个代码。

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

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

主线程的储存位置在那?在main函数中。

线程的四种状态

1被创建2运行3冻结4消亡

5阻塞状态,四个线程,cup只能运行一个,具备运行资格,但是没有运行权

 

线程都有自己的默认名称,Thread-编号 该编号从零开始。用getName()方法获取

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

getName() 获取线程名称

设置线程名称:setName或者构造函数。

 

第一种创建方式不靠谱了,因为比如说四个窗口同时卖一百张票,怎么弄?票的数量是固定的。

创建线程的第二种方式,实现Runable接口

步骤 1,定义类实现Runable接口

       2,覆盖Runable接口中的run方法

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

       3,通过Thread类建立线程对象。

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

                     为什么要将Runable接口的子类对象传递给Thread类的构造函数(因为,自定义的run方法所属的对象是Runable接口子类的对象,所以要让线程去指定指定对象的run方法,就必须明确该run方法所属的对象)

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

 

线程的实现方式和继承方式有什么区别呢(面试一定要说的出来,经常考)

(如果一个类已经继承了父类,并且这个子类中有run方法需要被多线程执行,但是java中只支持单继承,那怎么办呢,这时候就需要实现一个借口,去实现接口中的run方法,个人理解)

实现方式好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。

两种方式的区别:存放代码的位置不一样

继承Thread:  线程代码存放在Thread子类的run方法中。

实现Runable:线程代码存放在接口的子类run方法中。

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

解决办法,对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与。|(个人理解就是一个窗口卖完一张,下一个窗口才能卖,而不是一个窗口全吧票卖了。)

 

Java中对于多线程的安全问题提供了专业的解决方式,就是同步代码块

synchronizod(对象.)

{

       需要被同步的代码

}

 

那些语句在操作共享数据,就把他们放在同步代码块中     

原理,对象如同一把锁,第一个进来的线程在进入代码块之后会吧锁锁上,即使在代码块中被冻结了,释放了执行权,但是其他线程在近来的时候,会被锁挡在外面进行等待,直到同步代码块中的线程执行完,将锁打开,其他线程才可以运行。

同步的前提:1,必须是多线程,至少两个以上的线程,单线程还用同步吗

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

 

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

弊端:多个线程都需要判断锁,较为消耗资源(是在允许范围内的)

同步有两种方式

1同步代码块2,同步函数,吧同步代码快的修饰符放在函数就ok、

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

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

如果同步函数被静态修饰,使用的锁是类名.class。

 

死锁,同步中嵌套同步

 


原创粉丝点击