黑马程序员—多线程

---------------------- <a href="http://edu.csdn.net/heima" target="blank">android培训</a>、<a href="http://edu.csdn.net/heima" target="blank">java培训</a>、期待与您交流! ----------------------


详细请查看:<a href="http://edu.csdn.net/heima" target="blank">http://edu.csdn.net/heima</a>

进程:正在执行的程序。就是一个应用程序在内存中开辟的空间。

线程:其实就是进程中的一个控制单元。它负责就是程序的执行。

一个进程中至少有一个线程。JVM本身就是多线程的。因为在程序运行过程中会在堆内存产生很多的垃圾。就需要被垃圾回收器进行回收。main函数代码执行时,也在运行着垃圾回收。所以是同时执行的,这就是两个独立线程来进行控制的。执行垃圾回收的线程,称为垃圾回收线程。执行main函数的线程,称为主线程。

创建一个执行路径的目的就是,让单独一个线程去执行指定的代码。和其他代码同时执行。

对于主线程:它的运行的代码都存储在主函数中。

对于垃圾回收线程:它运行就是用于回收对象垃圾的代码。

描述线程的对象是 Thread 类。继承 Thread 类,覆盖其run方法。Thread 本身就是一个类,就是一个线程Thread,直接创建其对象,就是一个线程。run既然是运行方法,那么他里面存储了线程要运行的代码。可是我们想要创建线程运行我们自己制定的代码,那么这时就应该利用继承思想,将 Thread 类进行继承,并覆盖已经有的run方法,定义自己要运行的线程代码。

步骤:

1、继承 Thread 

2、覆盖 Thread 类中的run方法  在该方法中定义需要运行的代码

3、调用线程对象中的start方法开启线程,并调用线程的run方法

主线程运行的代码在主函数中,自定义线程运行的代码在run方法中。发现程序运行的结果每次都不一样。那是因为多线程的随机性造成的。CPU做着快速的切换,运行着各个线程。随机性原理是由于CPU做着快速的切换造成的。形象的称为多个线程在抢夺cpu的执行权。哪个线程获取到cpu的执行权,哪个线程就执行。

主线程名称为 “main”。其他中定义线程名称  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();//让线程开启并执行线程的run方法
  System.out.println("Hello World!");
 }
}
       注意:在某一时刻,只能有某一个程序在运行(多核除外),CPU在各个程序之间在做着快速的切换,已达到看上去同时运行的效果。这就是多线程的一个特性:随机性。
    class Demo extends Thread
{
 public void run()
 { 
  for(int x=0;x<60;x++)
  System.out.println(this.getName()+"demo run:");//获取线程名称,
 }
}
class ThreadDemo1 
{
 public static void main(String[] args) 
 {
  Demo d = new Demo();// 就创建了一个线程
  Demo d1 = new Demo();
  d.start();
  d1.start();
  for (x=0;x<70;x++ )
  {
   System.out.println(Thread.currentThread()+"Hello World!");
                                                               // 获取线程名称
  }
 }
}
       创建线程的另一种方法是声明实现Runnable接口的类。该类然后实现run方法。然后可以分配该类的实例。步骤:
               1、定义类实现Runnable接口;
               2、覆盖Runnable接口中的run()方法;
                           目的:将线程要运行的代码放在run方法中
               3、通过Thread类建立线程对象;
               4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
                          原因:因为自定义的run方法所属的对象时Runnable接口的子类对象,多以要让线程去执行指定对象的run方法,就必须明确该run方法 所属的对象。
               5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
四、继承方式和实现方式的区别:
     1、实现方式好处:避免单继承的局限性,还可以把run方法中的数据共享。再定义线程时,建议使用实现方式。
     2、两种方式的区别:继承Thread:线程代码存放在Thread子类的方法中。而实现Runnable:线程代码存放在Runnable接口子类的run方法中。
 class Ticket implements Runnable
{
 private  int tick=100;
 public void run()
 {
  while(true)
  {
   if(tick>0)
   {
    System.out.println( Thread.currentThread()+"..."+tick--);
   }
  }
 }

}
class  TicketDemo
{
 public static void main(String[] args) 
 {
  Ticket t=new Ticket();
  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();
 }
}
五、多线程的安全问题
   (一)同步代码块
class Ticket implements Runnable
{
 private  int tick=100;
 Object obj = new Object();
 public void run()
 {
  while(true)
  {
   
 /*
 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,
 还没有执行完,而另一个线程参与进来执行,导致共享数据的错误。解决办法
        对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他线程
 不可以参与执行。解决办法就是同步代码块:
    synchronized(对象)
   {
   需要被同步的代码;
    }
            
 */
       //同步的前提:
     //必须要有两个或者两个以上的线程;必须是多个线程使用同一个锁
  synchronized(obj)//同步代码块中的锁。持有锁的线程可以在同步中执行。
     {        //没有持有锁的线程即使获取可cpu的执行权,也进不去
        //因为没有获得锁
                if(tick>0)
      
  try
  {
  Thread.sleep(10)//让线程睡10ms该方法有异常抛出,由于接口没有抛异常
                      //所以子类必须处理
  }
  catch (Exception e)
  {
  }
  System.out.println(Thread.currentThread()+"..."+tick--);
      }
     }
  }
 }

}
class  TicketDemo
{
 public static void main(String[] args) 
 {
  Ticket t=new Ticket();
  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();
  
  
  
 }
}
 同步代码块的好处:解决了多线程的安全隐患。
 同步代码块的弊端:降低了程序执行的效率。
    (二)同步函数
     如何找问题:
      1、明确那些代码是被多线程运行的代码;
      2、明确共享数据;
      3、明确多线程运行代码中哪些语句是操作共享数据的。

class Bank
{
 private int sum;
 //Object obj = new Object();
 public synchronized void add(int n)//同步函数的用法
 {
  sum=sum+n;
  try{Thread.sleep(10);}catch(Exception e){}
  System.out.println("sum="+sum);
 }
}
class Cus implements Runnable
{
 private Bank b= new Bank();
 public void run()
 {
  for (int x=0;x<3;x++ )
  {
   b.add(100);
  }
 }
}
class  BankDemo
{
 public static void main(String[] args) 
 {
  Cus cus = new Cus();
  Thread t1 = new Thread(cus);
  Thread t2 = new Thread(cus);
  t1.start();
  t2.start();
 }
}
同步函数的锁:函数需要被对象调用,那么函数都有一个所属对象的引用this,所以同步函数所使用的锁是this。
   (三)静态同步函数
     静态进内存时,内存中还没有本类对象,但是一定有该类对应的字节码文件对象,
类名.class 该对象的类型是Class。那么静态的同步方法使用的锁是该方法所在类的字节码文件对象,类名.class。
    
//单例设计模式中的懒汉式
class Single
{
 private static Single s=null;
 private Single(){}
 public static Single getInstnce()
 {
  if(s==null)
  {
   synchronized(Single.class)
                             //使用的锁是该方法所属对象的字节码对象
   {
    if(s==null)
               s= new Single();
   }
  }
  return s;
 }
}
class SingleDemo
{
 public static void main(String[] args) 
 {
  
 }
}
        (四)死锁
       同步中嵌套着同步,而且这些同步使用不是同一个锁。
事例:
class test implements Runnable
{
 private boolean flag;
 test(boolean flag)
 {
  this.flag=flag;
 }
 public void run()
 {
  if(flag)
  {
   while(true)
   {
    synchronized(MyLock.locka)
    {
     System.out.println("if locka");
     synchronized(MyLock.lockb)
     {
      System.out.println("if lockb");
     }
    }
   }
  }
  else
  {
   while(true)
   {
    synchronized(MyLock.lockb)
    {
     System.out.println("else lockb");
     synchronized(MyLock.locka)
     {
      System.out.println("else locka");
     }
    }
   }
  }
 }
}
class MyLock
{
 static Object locka =new Object();
 static Object lockb =new Object();
}

class DeadLock 
{
 public static void main(String[] args) 
 {
  Thread t1 = new Thread(new test(true));
  Thread t2 = new Thread(new test(false));
  t1.start();
  t2.start();
 }
}

六、线程间的通信

   其实就是多个线程在操作同一个资源,但是操作的动作不同。
   (一)等待唤醒机制
class Res
{
 String name;
 String sex;
 boolean flag=false;
}
class Input implements Runnable
{
 private Res r;
 Input(Res r)
 {
  this.r=r;
 }
 public void run()
 {
  int x= 0;
  while(true)
   {   
    synchronized(r)
       {
    if(r.flag)
    try{r.wait();}catch(Exception e){}//让线程等待
    if(x==0)
    {
     r.name="mike";
     r.sex="man";
    }
    else
    {
     r.name="丽丽";
     r.sex="女";
    }
    x=(x+1)%2;
    r.flag=true;
    r.notify();//唤醒等待的进程
    }
   }
  }
}
class Output implements Runnable
{
 private Res r;
 Output(Res r)
 {
  this.r=r;
 }
 public void run()
 {
  while(true)
  {
   synchronized(r)
   {
    if(!r.flag)
     try{r.wait();}catch(Exception e){}
    System.out.println(r.name+"...."+r.sex);
    r.flag=false;
    r.notify();
   }
  }
 }
}
class  InputOutputDemo
{
 public static void main(String[] args) 
 {
  Res r= new Res();
  Input in = new Input(r);
  Output out = new Output(r);
  Thread t1 = new Thread(in);
  Thread t2 = new Thread(out);
  t1.start();
  t2.start();

 }
}
/*
wait(),notify(),notifyAll()都是用在同步中,因为要对持有监视器(锁)的线程操作,
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义在Object类中呢?
 因为这些方法在操作同步中的线程时,都必须要标示它们所操作线程持有的锁,
 只有在同一个锁上的被等待的线程,才可以被同一个锁上的notify()唤醒。不可以对不同锁中
 的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而所可以是任意的,所以可以被任意
 对象调用的方法都定义在Object类中。

*/

 

(二)生产消费者问题

class Resource
{
 private String name;
 private int count;
 private boolean flag= false;
 public synchronized void set(String name)
 {
  while(flag)
   try{this.wait();}catch(Exception e){}//让线程等待
  this.name=name+"___________________-__"+count++;
  System.out.println(Thread.currentThread().getName()+"生产"+this.name);
  flag=true;
  this.notifyAll();
 }
 public synchronized void out()
 {  
  while(!flag)
   try{this.wait();}catch(Exception e){}//让线程等待
  //获取当前线程的名字和资源的名字
  System.out.println(Thread.currentThread().getName()+"消费"+this.name);
  flag=false;
  this.notifyAll();
 }
}
class producer implements Runnable
{
 private Resource r;
 producer(Resource r)
 {
  this.r=r;
 }
 public void run()
 {
   while(true)
     r.set("商品");
 }
}
class consumer implements Runnable
{
 private Resource r;
 consumer(Resource r)
 {
  this.r=r;
 }
 public void run()
 {
 while(true)
     r.out();
 }
}
class  ConsumerProducerDemo
{
 public static void main(String[] args) 
 {
  Resource r = new Resource();
  producer pro=new producer(r);
        consumer con = new consumer(r);
  Thread t1=new Thread(pro);
  Thread t2=new Thread(pro);
  Thread t3=new Thread(con);
  Thread t4=new Thread(con);
  t1.start();
  t2.start();
  t3.start();
  t4.start();
 }
}

七、1.5新特性

(一)加锁解锁(lock(),unlock())
   提供显式的锁机制:
import java.util.concurrent.locks.*;
class Resource
{
 private String name;
 private int count;
 private boolean flag= false;
 private Lock lock=new ReentrantLock();
 private Condition condition_pro=lock.newCondition();
 private Condition condition_con=lock.newCondition();
 public  void set(String name)throws InterruptedException
 {
  lock.lock();
  try
  {
   while(flag)
   condition_pro.await();//让线程等待
   this.name=name+"___________________-__"+count++;
   System.out.println(Thread.currentThread().getName()+"生产"+this.name);
   flag=true;
   condition_con.signal();
  }
  finally
  {
   lock.unlock();
  }
  
 }
 public  void out()throws InterruptedException
 {  
  lock.lock();
  try
  {
   while(!flag)
   condition_con.await();//让线程等待
  //获取当前线程的名字和资源的名字
  System.out.println(Thread.currentThread().getName()+"消费"+this.name);
  flag=false;
  condition_pro.signal();
  }
  finally
  {
   lock.unlock();
  }
  
 }
}
class producer implements Runnable
{
 private Resource r;
 producer(Resource r)
 {
  this.r=r;
 }
 public void run()
 {
   while(true)
     try
     {
   r.set("商品");
     }
     catch (InterruptedException e)
     {

     }
 }
}
class consumer implements Runnable
{
 private Resource r;
 consumer(Resource r)
 {
  this.r=r;
 }
 public void run()
 {
 while(true)
    try
    {
  r.out();
    }
    catch (InterruptedException e)
    {
    }
 }
}
class  ConsumerProducerDemo1
{
 public static void main(String[] args) 
 {
  Resource r = new Resource();
  producer pro=new producer(r);
        consumer con = new consumer(r);
  Thread t1=new Thread(pro);
  Thread t2=new Thread(pro);
  Thread t3=new Thread(con);
  Thread t4=new Thread(con);
  t1.start();
  t2.start();
  t3.start();
  t4.start();
 }
}
二、停止线程
   停止线程只有一种方法,那就是让run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制循环,就可以让run方法结束,也就是让线程结束。特殊情况下,当线程处于冻结状态,就不会读取到标记。就需要 interrupt清除冻结状态的线程。
  守护线程setDaemon(true)前台线程结束后,保护线程立即结束。
   join()向主线程要执行权。当A线程执行到了B线程的join方法时,A就会等待,等B线程都执行完,A线程才会执行。用于临时加入线程。
  yield()临时暂停线程




---------------------- <a href="http://edu.csdn.net/heima" target="blank">android培训</a>、<a href="http://edu.csdn.net/heima" target="blank">java培训</a>、期待与您交流! ----------------------


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值