黑马程序员——多线程

黑马程序员——线程

-----------android培训、java培训、java学习型技术博客、期待与您交流!------------

一、进程与线程

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

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

  一个进程中至少有一个线程。

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

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

二、多线程的意义

多线程提高了cpu的使用率。从而提高应用程序的使用率。线程和线程共享“堆内存和方法区内存”,栈是独立的,一个进程一个栈。

三、线程的创建方式

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

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

创建多线程的第一种方式:继承Thread

步骤:

1.定义类继承Thread

2.复写Thread类中的run方法

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

3.调用线程的start方法

class ThreadDemo{

    public static void main(String[] args) {

        //创建好一个线程

        Create td = new Create();

        //启动线程,而不是直接调用run方法。线程的执行是由java线程调度机制完成的。

        td.start();

td.run();//仅仅是对象的调用方法,而线程创建了,并没有运行。

        for(int x = 0; x < 200; x++)

        {

            System.out.println("Demo " + x);

        }

    }

}

 

//创建一个类,继承之Thread

class Create extends Thread

{

    //复写Thread类中的run方法。定义自己的内容

    public void run(){

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

            System.out.println( "Create " + x);

        }

    }

}

最后发现运行结果每次都不同。因为多个线程都获取cpu的执行权。Cup执行倒水,谁就运行。明确一点,在某一时刻,只能有一个程序在运行,CPU在做着快速切换,已达到貌似同时运行的效果。

2.为什么要覆盖run方法?

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

 

3.线程的5中状态

 

 

sleep

                                                         sleep结束 

wait();

notify()

stop()

run方法结束

 

 

 

 

 

 

4.线程对象以及名称

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

线程的几个方法:

1.static Thread currentThread():获取当前线程对象。

2.getName():获取线程的名称。

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

5. 创建线程的第二种方式:实现Runnable接口。

步骤:

1.定义类实现Runnable接口;

2.覆盖Runnable接口中的run方法;

3.通过Thread类建立线程对象;

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

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

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

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

 

 

 class RunnableThreadDemo {

    public static void main(String[] args) {

        //创建卖票类实例对象

        Ticket t = new Ticket();

        //将通过Thread类建立线程对象;

        Thread t1 = new Thread(t);

        Thread t2 = new Thread(t);

        Thread t3 = new Thread(t);

        Thread t4 = new Thread(t);

        //启动线程

        t1.start();

        t2.start();

        t3.start();

        t4.start();

 

    }

}

 

//实现Runnable接口

class Ticket implements Runnable

{

    private int ticket = 100;

    public void run() {

        while(true)

        {

            if(ticket > 0)

            {

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

            }

        }

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

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

  两种方法的区别:

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

实现Runnable:线程代码存在接口的子类的run方法

四、多线程的特性

 1.多线程的安全问题:

通过分析,发现上述程序打印出0-1-2等错票。说明多线程运行出现了安全问题。

问题的原因:

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

解决方法:

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

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

synchronized(对象)

{

       需要被同步的代码

}

class Ticket implements Runnable

{

private int ticket = 100;

Object obj=new Object();

    public void run() {

        while(true)

        {

            synchronized(obj){

                if(ticket > 0)

                {

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

                 }

             }

        }

同步代码块如同给对象加锁,持有锁的线程可以在同步中执行,

没有持有锁的线程即使获取了cpu的执行权,也进不到同步代码块中,因为没有获取锁。

同步的前提:

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

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

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

好处和弊端:

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

弊端:多个线程需要判断锁,较为消耗资源。

同步有两种表示:同步代码块和同步函数。

如何找同步:

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

2.明确共享数据。

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

 

2.单例设计模式-懒汉式

Class Single{

Private static Single s=null;

Private Single(){}

Public static synchronized Single getInstance(){

  if(s==null){

     Synchronized(Single.class){

    if(s=null)

        s=new Single();

    }

}

return s;

 

3.停止线程

停止方法1run方法结束。开启多线程运行,运行代码通常是循环结构。

只要控制住循环,就可以让run方法结束,也就是线程结束。

 

特殊情况:

   当线程处于了冻结状态,就不会读取到标记。那么线程就不会结束。

当没有指定的方式让冻结的线程恢复带运行状态时,这需要对冻结进行清除。

停止方法2:使用interrupt()方法。结束线程的冻结状态(sleepwaitjoin方法),使线程回到运行状态中来。也就是靠异常机制来结束线程。

4.等待唤醒机制

  1. wait();notify();notifyall();都是用在同步中,因为要对持有监视器(对象锁)的线程操作,而同步中才有锁。

  2.这些操作方法定义在Object类中。因为这些方法在操作同步中线程时,都必须要标识他们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值