黑马程序员_java基础-多线程

------- <a href="http://www.itheima.com"  target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! -------

多线程

在同一刻运行多个任务的能力称为多任务(multitasking),每一个任务称为一个线程(thread),可以同时运行一个以上线程的程序称为多线程程序(multithreaded).线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行.一个进程中至少有一个线程.

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

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

创建新执行线程有两种方法.一种方法是将类声明为Thread的子类.该子类应重写thread类的run方法.接下来可以以分配并启动该子类的实例.另一种方法是声明实现Runnable接口的类.该类然后实现run方法.然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动.

为什么要覆盖run方法?

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

创建线程

1.实现代码:

public class CreateThreadDemo

{

     public static void main(String[] args)

     {

//创建一个线程

         NewThread nt = new NewThread();

        

         nt.start();//开启线程,并调用run()方法

        

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

         {

              System.out.println("------world" + i);

         }

     }

    

    

}

class NewThreadextends Thread

{

     public void run()

     {

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

         {

              System.out.println("hello" + i);

         }

     }

}

从上面运行的结果可以看出多线程的一个特性:随机性.并且多线程都在获取CPU的使用权,CPU执行到谁,谁就运行.CPU在做着快速的切换,以达到看上去同时运行的效果.但某一时刻只能有一个程序在运行(多核除外)

 

 

2.实现代码:

步骤:

Ø      定义类实现Runnable接口

Ø      覆盖Runnable接口中的run方法

Ø      通过Thread类建立线程对象

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

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

public class CreateThreadByInterface

{

     public static void main(String[] args)

     {

         // 实例化对象

         SaleTicket st = new SaleTicket();

 

         // 创建线程

         Thread thread1 = new Thread(st);

         Thread thread2 = new Thread(st);

         Thread thread3 = new Thread(st);

 

         // 启动线程并运行st.run方法

         thread1.start();

         thread2.start();

         thread3.start();

 

     }

 

}

 

class SaleTicketimplements Runnable

{

     int ticket = 100;

 

     public void run()

     {

         while (ticket > 0)

         {

              /*

              try

              {

                   Thread.sleep(10);

              }

              catch (InterruptedException e)

              {

                   e.printStackTrace();

              }*/

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

                       + (ticket--));

         }

     }

}

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

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

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

实现方式好处:避免了单继承的局限性,在定义线程时,建议使用实现方式.从上面的代码可以看出实现方式把ticket资源独立出来,多个线程操作同一个资源.

     线程安全


以上图形描述了线程的四种状态:当调用sleep()和wait()方法时线程被冻结,当调用wait()冻结线程时要唤醒线程则必须使用notity()方法.当调用stop()方法时和线程运行结束时,该线程消亡.几个线程同时运时而某一刻CPU中只能有一个在运行,那行其它的线程将处于阻塞状态或者叫临时状态.当几个线程共享数据时,当线程正处理共享数据还没有执行完,而另一个参与进来执行,那么就将可能出现线程安全问题,导致共享数据错误.

如把上面代码: /*

              try

              {

                   Thread.sleep(10);

              }

              catch (InterruptedException e)

              {

                   e.printStackTrace();

              }*/

注释取消那么再运行程序,就很有可能会出现票已经卖出完毕却还在卖票的情况.打印结果很可能出现ticket等于0或0以下.这就出现了安全问题.

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完.在执行过程中,其它的线程不可以参与执行.java对于多线程的安全问题提供了专业的解决方式”同步代码块”

synchronized(Object obj)

{

     //需要被同步的代码……

}

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

     同步的前提:

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

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

必须保证同步中只能有一个线程在运行.同步虽然解决了安全问题,但是也降低了效率.因为每次都得去判断锁.较为消耗资源.就好比门上了锁.开锁需要时间一样.同步有两种表现形式:同步代码块,同步函数

同步(锁)演示

实现代码:

/**

 * 需求:有两个储户分别存300元,每次存100元,存3次.

 * 目的:练习线程安全

 * 如何分析线程安全:

 *   1.明确哪些代码是多线程代码

 *   2.明确共享数据

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

 */

public class SynchronizedDemo

{

     public static void main(String[] args)

     {

         Depositor dep = new Depositor();//实例化对象

 

         Thread th1 = new Thread(dep);//创建线程

         Thread th2 = new Thread(dep);//创建线程

 

         th1.start();// 开启线程并运行run方法

         th2.start();

     }

}

 

// 银行类

class Bank

{

     int sum;

     public synchronized void add(int n)//同步函数

     {

         //synchronized (this)同步代码块

         //{

              sum += n;

              try

              {

                   Thread.sleep(10);//停10毫秒

              }

              catch (InterruptedException e)

              {

                   e.printStackTrace();

              }

              System.out.println("sum:" +sum);

         //}

     }

}

 

// 储户类

class Depositorimplements Runnable

{

     // 用户到银行去存钱,实例化对象

     Bank bank = new Bank();

 

     public void run()

     {

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

         {

              // 调用银行的存款功能 .每次存100,存3次

              bank.add(100);

         }

     }

}

函数需要被对象调用,那么函数都有一个所属对象引用(this),因此同步函数所使用的锁是this.如果同步函数被静态修饰后使用的锁不再是this.因为静态方法中不可以定以this.静态进内存中,没有本类对象,但一定有该类对应的字节码文件对象.因而使用用的锁是该静态所对应的字节码文件对象.那么该静态所使用的锁就是(类名.class),类型是Class

 

     静态修饰的函数的使用的锁代码演示:

public class SynchronizedDemo3

{

     public static void main(String[] args)throws InterruptedException

     {

         // 实例化对象

         SaleTicket2 st = new SaleTicket2();

 

         // 创建线程

         Thread thread1 = new Thread(st);

         Thread thread2 = new Thread(st);

 

         // 启动线程并运行st.run方法

         thread1.start();

         //因为有可能主线程执行完毕的时候,第一个线程并未执行,因此先冻结

         Thread.sleep(10);

         st.flag = false;

         thread2.start();

     }

}

class SaleTicket2 implements Runnable

{

     static int ticket = 100;

     boolean flag = true; //定义一个标记,让线程在不同的地方同步运行

 

     public void run()

     {

         if (flag)

         {

              while (true)

              {

                   //synchronized使用的是字节码文件对象(SaleTicket2.class)

                   synchronized (SaleTicket2.class)

                   {

                       if (ticket > 0)

                       {

                            try

                            {

                                 Thread.sleep(4);

                            }

                            catch (InterruptedException e)

                            {

                                 e.printStackTrace();

                            }

                            System.out.println(Thread.currentThread().getName()

                                     + "this&&" + (ticket--));

                       }

                   }

              }

         }

         else

         {

              show();

         }

     }

 

     //静态修饰的同步函数,他所使用的锁是SaleTicket2.class

     public static synchronized void show()

     {

         while (ticket > 0)

         {

              try

              {

                   Thread.sleep(10);

              }

              catch (InterruptedException e)

              {

                   e.printStackTrace();

              }

              System.out.println(Thread.currentThread().getName()+"show**"

                       + (ticket--));

         }

     }

}

 

线程停止的方法只有一种run运行结束(stop()已过时)!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值