黑马程序员--Java基础之多线程(1)

来源:互联网 发布:万里の长城知乎 编辑:程序博客网 时间:2024/06/08 08:26
------------------ android培训、java培训、期待与您交流! ---------------------

一、线程概述

1、进程:正在执行中的程序。每一个进程执行都有一个执行顺序。

   该顺序是一个执行路径,或者叫一个控制单元。

2、线程:进程中的一个独立的控制单元。每个进程中都由一个线程或多个线程组成。

二、创建线程的两种方法

1、继承Thread类,复写其run方法。然后创建该子类的对象。

2、实现Runnable接口,复写其run方法,然后创建Thread类,并使用Runnable的子类对象初始化Thread类对象。

   使用run方法用来存放该线程要运行的代码。注意除了主方法,只有使用start方法才可开启一个新的线程。多个线程同时运行,其实是随机的,哪个线程抢到CPU的执行权,哪个线程运行。但是某一个时刻肯定是一个线程在运行,只不过运行速度快,,给人的感觉是多个线程在同时运行。

三、线程的运行状态图解:


四、多线示例(卖票示例):

//练习:模拟卖票系统,有三个窗口同时在卖票public class ThreadDemo {public static void main(String[] args) {SellTicket s = new SellTicket();Thread t1 = new Thread(s);Thread t2 = new Thread(s,"我是第二个线程");//可以初始化线程的名字,未被初始化的线程会有系统默认名称Thread t3 = new Thread(s);t1.start();//开启三个线程,相当于三个窗口 都在卖票t2.start();t3.start();}}//模拟卖票系统,实现Runnable方法要比继承Thread方法不容易出错,class SellTicket implements Runnable{private int ticketNum = 200;@Overridepublic void run() {while(ticketNum>0){//当票数不为0时,票数减1,相当于在卖票ticketNum = ticketNum-1;//Thread.CurrentThread()表示当前线程,.getName()用来获取该线程的名字System.out.println(Thread.currentThread().getName()+"===总票数还有"+ticketNum+"张!");}}}

注意,每一个线程建立时,都会开辟一片内存空间,用来存放局部变量,局部变量是新的了,这将不利于数据共享,所以卖票建议使用实现方式完成。

三、线程的安全机制---同步

1、为什么不安全?

问题原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

          另一个线程就参与进来执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,规定一次只能一个线程执行,执行完后,其他线程才能进来执行。

2、 同步代码块。

解决以上问题,可以使用synchronized(对象){需要被同步的代码}

   明确多线程中哪些代码操作共享数据,就可以将他们同步起来。

   同步代码块的锁是其参数对象。谁持有锁谁就可以执行同步代码。

   同步安全,但是较为消耗资源。

同步的前提:

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

2)必须是多个线程使用同一个锁。共享同一块数据。

synchronized (obj) {while (ticketNum > 0) {//当票数不为0时,票数减1,相当于在卖票ticketNum = ticketNum - 1;//Thread.CurrentThread()表示当前线程,.getName()用来获取该线程的名字System.out.println(Thread.currentThread().getName()+ "===总票数还有" + ticketNum + "张!");}}

3、同步函数。

   可以将函数变成同步函数,只需要在函数返回类型前加上关键字synchronized。同步函数的锁是this

   静态同步函数的锁是class对象,即某个类的class文件如:Person.class

4、同步原理:

   用标志位(监视器/锁)来控制。刚进同步块就判断标志位,并改变标志位,出去后再改回来标志位。

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