黑马程序员——多线程

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程:线程是进程的一个实体,是CPU调度和分派的基本单位,能够独立执行,有时被称为轻量级进程,是进程中执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

进程与线程的关系:
线程是进程中的一个执行单元,也可说进程有线程组成,一个进程至少一个线程。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。

多线程:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
多线程的意义:因为是并发执行线程,能够有效地提高执行效率,以及充分利用系统资源。

线程的创建:

方式一:继承Thread类
通继承Tread类并重写run方法,并把要在该线程中执行的代码放在run方法中编写,
Thread类的部分方法:
getName():返回该线程的名称。
isAlive():测试线程是否处于活动状态。
run():存放执行代码
sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
stop():用来终止线程将释放它已经锁定的所有监视器
yield():暂停当前正在执行的线程对象,并执行其他线程。
join():等待该线程终止

/**
     * 多线程
     */
    public static void main(String[] args) {
        //新建一个线程
         ThreadTest thread = new ThreadTest();
         //运行run方法
         thread.run();
         //主程序的运行代码
         for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);
    }

}
//继承Thread并重写run方法
class ThreadTest extends Thread
{
     //重写run方法
    @Override
    public void run() {
        for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);
    }

}

运行结果:
这里写图片描述

总运行的结果看,代码的执行过程都是有主线程main在执行,并没有产生新的线程。执行run 方法是不能产生新的线程的。

/**
     * 多线程
     */
    public static void main(String[] args) {
        //新建一个线程
         ThreadTest thread = new ThreadTest();
         //用start启动线程
         thread.start();
         //主程序的运行代码
         for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);
    }

}
//继承Thread并重写run方法
class ThreadTest extends Thread
{
     //重写run方法
    @Override
    public void run() {
        for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);
    }

}

运行结果:
这里写图片描述

从运行的结果看,看到了两个线程名,两线程是同时执行的,看到的结果是两个线程的结果交替打印,说明线程必须有start方法启动,start方法会去调用run方法。

方式二:实现Runnable接口
通过实现Runnable接口,并实现run方法,把Runnable子类对象作为参数传到新建一个Thread的线程。

/**
     * 多线程
     */
    public static void main(String[] args) {
        //新建一个线程
          RunnableTest run = new RunnableTest();
          Thread thread = new Thread(run);
          //用start启动线程
          thread.start();
         //主程序的运行代码
         for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);
    }

}
//继承Thread并重写run方法
class RunnableTest   implements Runnable
{
   //重写run方法
    @Override
    public void run() {
        for(int i= 0;i<5;i++)
        System.out.println(Thread.currentThread().getName()+"------"+i);

    }

}

运行结果:
这里写图片描述

可以看到上下两个线程的执行代码是一样的,所以说他们的代码效果是一样的,用两种方法都可以创建新的线程。那么为什么要有两种方法呢?用哪一种好呢?
我们知道java是单继承的,如果我们用方法一继承Thread方法,那该类就不能继承其他类,这样不利于程序的扩展性,而java中可以实现多接口,所以java中增加了一个实现接口的方法避免了java单继承带来的影响。一般建议采用第二种方法,能够继承其他类的同时,也可以实现多线程。

线程的生命周期:

创建:线程对象的生成
临时状态:线程对象调用start()方法,线程对象可以运行,具有了执行权,但不一定执行线程,因为有可能其它线程正在执行,占用了CPU的资源,这时候的线程被阻塞。
运行状态:线程从就绪状态获得了CPU资源,线程执行,。
冻结:执行的线程调用了sleep()或wait()方法,线程睡眠或等待,睡眠结束或者用notify()唤醒等待,线程可以回到临时状态
消亡:线程运行结束

结构:
这里写图片描述
练习:

/**
 * 买票程序:
 * 同时建立多个窗口进行买票,窗口实现Rnunable,
 * 可以同时运行多个窗口进行买票
 */
public class Test12 {


    public static void main(String[] args) {
        //建立Ticket对象
        Ticket ticket = new Ticket();
        //建立线程
        Thread thread1 =new Thread(ticket);
        Thread thread2 =new Thread(ticket);
        Thread thread3 =new Thread(ticket);
        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();

    }

}
//买票窗口
class Ticket   implements Runnable
{
    //票数
    private  int ticket = 100;
   //重写run方法
    @Override
    public void run() {

        while(ticket>0)
        {
             //打印买票的线程,以及票的剩余量
            try {
                //买票消耗的时间
                Thread.sleep(10);
            } catch (InterruptedException e) {

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

    }

}

运行结果:
这里写图片描述
从运行结果看到,每一个线程中卖出票后,飘的剩余量,但是从结果中看到了票数出现-1、-2。票数怎么可以是负数呢?明显说明了程序运行与我们想要的结果不同,那么程序出错在哪?
当多个线程在操作一个对象的数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。

线程安全

如何解决数据共享时出现的数据错误,出现错误是因为一个线程访问一个数据的动作还没有做完时,另一个线程获得了执行的资源,再次对数据进行了操作,当前一个线程回来再次执行还没有执行完的代码时,因为已经有另一线程对数据可能进行了修改,所以当线程要把剩余代码执行完时,有可能会出现前后两次的数据不对,造成数据出错。
为了避免数据的共享时出现错误,那就要求当一个线程执行一次数据的操作是时,其他的线程不能进入对数据进行操作,直到该线程执行完这一次操作代码,这样保证了数据共享的安全,这样的设定叫做同步:
如何实现同步?

用关键字synchronized来实现:
1、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁:
格式:
2、同步函数:
在函数上加上synchronized修饰符即可,同步函数的锁是this。

加上了同步后的程序:

/**
 * 买票程序:
 * 同时建立多个窗口进行买票,窗口实现Rnunable,
 * 可以同时运行多个窗口进行买票
 */
public class Test12 {


    public static void main(String[] args) {
        //建立Ticket对象
        Ticket ticket = new Ticket();
        //建立线程
        Thread thread1 =new Thread(ticket);
        Thread thread2 =new Thread(ticket);
        Thread thread3 =new Thread(ticket);
        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();

    }

}
//买票窗口
class Ticket   implements Runnable
{
    //票数
    private  int ticket = 100;  
   //重写run方法
    private Object obj=new Object();
    @Override
    public void run() {
        {
        while(ticket>0)
        {
            try {
                //买票消耗的时间
                Thread.sleep(10);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
             //打印买票的线程,以及票的剩余量 
            synchronized(obj)
            {
                //这次判断ticketshi是否大于0
              if(ticket>0)
              {

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

运行结果:

这里写图片描述

同步死锁
同步解决了多线程的数据共享的时可能造成的错误,但是也带来了一个问题,多线程间可能会出现同步死锁:当两个线程同时执行时,而在代码在有两个锁,当每一个线程各占用了一个锁时,而这时候两个线程都要得到对方的锁在可以继续执行下去时,这两线程就会在阻塞状态,等待对方放出锁,而这时候的两个线程都不会放出当前的锁,这样就造成程序无法执行下去。
死锁代码:

/**
 *  同步死锁
 *  
 */
public class Test12 {


    public static void main(String[] args) throws InterruptedException {
         //启动两线程
         RunTest run = new RunTest();
         Thread thread1 = new Thread(run);
         Thread thread2 = new Thread(run);
         thread1.start();
         Thread.sleep(10);
         run.flag = false;
         thread2.start();
    }

}
//线程的两个锁
class Lock 
{
    public static Object lock1 = new Object();
    public static Object lock2 = new Object();
}
//线程
class RunTest implements Runnable
{
     public    boolean flag = true;
    @Override
    public void run() {
        if(flag)
        {
          while(true)
          {
              synchronized(Lock.lock1)
              {

                  synchronized(Lock.lock2)
                  {
                      System.out.println("true");
                  }
              }
          }
        }else
        {
            while(true)
            {
                synchronized(Lock.lock2)
                  {
                      synchronized(Lock.lock1)
                      {
                          System.out.println("false");
                      }
                  }
            }
        }

    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值