黑马程序员——多线程

来源:互联网 发布:青辰软件 编辑:程序博客网 时间:2024/06/05 23:56

---------------------- android培训java培训、期待与您交流! ----------------------

一、进程
   进程是在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
二、线程
    线程是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程中至少有一个线程。
三、自定义多线程
   创建新执行线程的两种方法:一种方法是将类声明为Thread的子类。该子类应重写Thread类的run方法。用于存储线程要运行的代码。定义多线程的步骤:
      1、定义类继承Thread;
      2、覆写run方法;
              目的是:将自定义的代码存储在run方法中,让线程运行。
      3、创建对象就是新建一个线程;
      4、调用start方法启动线程,start作用是启动线程,调用run方法。
事例:       
 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()临时暂停线程

 

---------------------- android培训java培训、期待与您交流! ----------------------

原创粉丝点击