现在的位置: 首页 > 综合 > 正文

黑马程序员-Java编程知识总结多线程

2018年05月23日 ⁄ 综合 ⁄ 共 12827字 ⁄ 字号 评论关闭

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


-Java编程知识点总结

.多线程。

1.         什么是进程:进程是正在运行的程序。2.运行中的电脑看上去可以同时执行多个程序其实CPU同一时刻只能运行一个程序,之所以看上去是在同时运行,是因为CPU在不停的切换执行。每一个进程执行都一个执行顺序,该顺序是一个执行路径或者叫一个执行单元。

2.         什么是线程:线程就是进程中的一个独立的单元,线程在控制进程的执行。

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

.自定义线程。

Java已经提供了对线程这类食物的描述。Thread类。

1.         创建线程的第一种方式:继承Thread类方法。

1,定义类继承Thread

2,复写Thread类中的run方法。目的:将自定义代码存储在run   方法。让线程运行。

3,调用线程的start方法,该方法两个作用:启动线程,调用run方法。

class Demoextends Thread

{

public void run ()

{ 

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

   {

      System.out.println(i);

   }

}

 

}

classThreadDmo

{

public static void main(String[] args)

{

   Demo d = new Demo();//创建继承自Thread类的子类对象。

   d.start();//start作用是开启线程,并调用run()方法。

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

      System.out.println("HelloWorld!--"+x);

}}

 
 


 

练习一:

创建两个线程,和主线程交替运行。线程都有自己默认的名称,Thread-编号的形式,编号是从0开始。Thread-0;

获取当前线程对象:static Thread currentThread();

设置线程的名称:setName();

class Testextends Thread

{

//private String name;

Test(String name)

{

   //this.name = name;

   super(name);//调用父构造函数,将对象名称传给父。

}

public void run()

{

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

//获取当前线程的对象获取当前线程对象的名称。

//该方法写于run方法中,即写于当前线程中使用。

//那个线程运行当前的run,就获得谁的对象t1,或t2

{System.out.println((Thread.currentThread()==this)+"..."+this.getName()+"run..."+x);

   }

}

 

}

 

 

 
 


class ThreadTest

{

public static void main(String[] args)

{

   Test t1 = new Test("one---");

   Test t2 = new Test("two+++");

   t1.start();

   t2.start();

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

   {

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

   }

}

}

 

2.多线程状态图:

 

 3.(必须掌握)练习二:售票。

class Tickets extends Thread

{privateint ticket = 100;

 Tickets(String name)

 {

    super(name);//必须会使用,1.使用构造函数2.调用父构造3.传对象的名称。

 

 }

 publicvoid run()

 {

  while (ticket>0)

  {

     ticket-=1;

     System.out.println(Thread.currentThread()+"sales"+ticket+this.getName());

  }

 

 }

 

}

class TicketDemo

{

 publicstatic void main(String[] args)

 {

    Ticketst1 = new Tickets("线程1");

    Ticketst2 = new Tickets("线程2");

    Ticketst3 = new Tickets("线程3");

    Ticketst4 = new Tickets("线程4");

    t1.start();

    t2.start();

    t3.start();

    t4.start();

 }

}

2.创建线程的第二种方式。

 1.实现Runnable接口。

 2.重写run()方法。

 3.*)通过Thread类创建线程对象。

 4.使用Thread类创建线程对象。将实现自Runnable接口的子类对象作为参数传递给Thread类的构造方法。因为自定义对象的run方法是Runnable接口的抽象方法run方法是子类的方法。而真正能使run方法运行的是Thread类所以要让线程运行run就得把Runnable的子类对象传递给Thread

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

 6.实现方式和继承方式的区别:使用Runnable接口可以实现多继承,即当继承了Thread类,子类就不能再继承其他类。当实现自Runnable接口,子类即创建了线程,又可以再继承其它的类。 

 5多线程安全问题

多线程安全问题出现的原因:

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

如下所示:

class Tickets extends Thread

{privateint ticket = 100;//该数据是4个线程共享的数据

 Tickets(String name)

 {

    super(name);

 

 }

 //run方法是4个线程共同执行的方法。存在于每个线程中,不是共享的。X定义在函数中属于局部是非共享,ticket是定义在类中的是共享的

 publicvoid run() //throws RuntimeException子类只能抛父类的子集,父类没有抛。所以不能抛出。

 {intx

  while (ticket>0)

       

    { try

        {

          Thread.sleep(50);//throwsInterruptedException该方法在使用时自动抛出异常,所以要处理。

           

    //有可能出现以下情况1.2.3.4线程。当ticket
= 1
时,1线程获取执行权,打印1;然后自减为0

    //这时2线程获得了执行权,又打印了一次0,自减为-1.这时3线程执行,又打印了-1.这些线程就没有进行条件判断,在这里输出了错值。

           System.out.println(this.getName()+"sales"+ticket--);

        }

        catch (Exception e)

        {

 

        }

  }

 

 }

 

}

class TicketDemo

{

 publicstatic void main(String[] args)

 {

    Ticketst1 = new Tickets("线程1");

    Ticketst2 = new Tickets("线程2");

    Ticketst3 = new Tickets("线程3");

    Ticketst4 = new Tickets("线程4");

    t1.start();

    t2.start();

    t3.start();

    t4.start();

 }

 

1.*)解决办法对多条操作共享数据的语句只让一个线程执行完,其他线程都不可以参与。使用同步代码块。

synchronized(对象)

{

 需要被同步的代码

 

}对象如同锁。持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

把上个例子的代码修改为安全的代码:

Object obj = new Object();

public void run() 

{

//锁就是对象。当线程t1.t2执行到这时,

      // 1.假如t1获得了锁,锁标0->1

          4.t2获得了执行权,t2进入,执行输出语句。

              Synchronized(obj)
锁标(0)禁止(1)进入

 {//2.t1线程进入后锁标1->0;t2进不来。

   while (ticket>0)

   { 

          try

        {

          Thread.sleep(50);//线程会在这儿冻结,

当时间到后,转入到阻塞状态等cpu执行权。

System.out.println(this.getName()+"sales"+ticket--);

        }

        catch (Exception e)

        {

 

        }

  

   }

    //3.t1执行完后退出0->1;t1跳出锁。

 }

}

    在锁标处的执行情况:

    1.假如t1持有了锁,锁标0->1

    2.t1线程进入后锁标1->0;t2进不来。

    3.t1执行完后退出0->1;t1跳出锁。

    4.此时可进入,t2获得了执行权,t2进入,执行输出语句。

    总结:对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

    同步的前提:

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

      2,必须是多个线程使用同一个锁。(同一个对象)

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

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

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

1.         *)同步函数:使函数具有同步性。在函数的返回类型前加上synchronized.所以到这里同步方法有两种:

       1.同步函数:使用synchronized作为修饰放在返回类型的前边。        2.同步代码块,使用synchroized(对象){}.的方式实现同步。

    如何找问题:

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

    2,明确共享数据。

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

class Bank

{

 privateint sum;//sum是共享数据所以要把操作共享数据的语句封装为同步语句。

 //Objectobj = new Object();

 publicsynchronized void add(int n)

 {

    //synchronized(obj)

    //{

       sum= sum + n;

       try{Thread.sleep(10);}catch(Exceptione){}

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

    //}

 }

}

 

class Cus implements Runnable

{

 privateBank b = new Bank();

 publicvoid run()

 {    

    for(intx=0; x<3; x++)

    {

       b.add(100);//调用了包含共享数据的方法。

    }

 }

}

 

 

class BankDemo

{

 publicstatic void main(String[] args)

 {

    Cusc = new Cus();

    Threadt1 = new Thread(c);

    Threadt2 = new Thread(c);

    t1.start();

    t2.start();

 }

}

练习:使用同步函数的方式重写售票的例子。

2.         同步函数的锁。

 同步函数用的是哪一个锁呢?函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this

下面的实例来验证上面的结论:

    通过该程序进行验证。使用两个线程来买票。一个线程在同步代码块中。一个线程在同步函数中。

都在执行买票动作。

class Ticket implements Runnable

{

 private int tick = 100;

 Objectobj = new Object();

 booleanflag = true;

 public void run()

 {

    if(flag)

    {

       while(true)

       {//当锁使用obj时,线程会出现打印错误的票数,表明线程出现了不同步的情况,当时用this时,打印正确。表明this时同步函数的锁。

          synchronized(this)

          {

            if(tick>0)

            {

               try{Thread.sleep(10);}catch(Exceptione){}

             System.out.println(Thread.currentThread().getName()+"....code: "+ tick--);

            }

          }

       }

    }

    else

       while(true)

          show();

 }

 publicsynchronized void show()//this

 {

    if(tick>0)

    {

       try{Thread.sleep(10);}catch(Exceptione){}

     System.out.println(Thread.currentThread().getName()+"....show....: "+ tick--);

    }

 }

}

 

class ThisLockDemo

{

 publicstatic void main(String[] args)

 {

    Tickett = new Ticket();

//两个线程操作的是同一个对象。共享数据是ticket

    Threadt1 = new Thread(t);

    Threadt2 = new Thread(t);

    t1.start();

    try{Thread.sleep(10);}catch(Exceptione){}

    t.flag= false;

    t2.start();

 }

}

3.         静态同步函数的锁

    静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class该对象的类型是Class

class Ticket implements Runnable

{

 privatestatic int tick = 100;

 //Objectobj = new Object();

 booleanflag = true;

 public void run()

 {

    if(flag)

    {

       while(true)

       {

          synchronized(Ticket.class)

          {

            if(tick>0)

            {

               try{Thread.sleep(10);}catch(Exceptione){}

             System.out.println(Thread.currentThread().getName()+"....code: "+ tick--);

            }

          }

       }

    }

    else

       while(true)

          show();

 }

 publicstatic synchronized void show()

 {

    if(tick>0)

    {

       try{Thread.sleep(10);}catch(Exceptione){}

     System.out.println(Thread.currentThread().getName()+"....show....: "+ tick--);

    }

 }

}

 

 

class StaticMethodDemo

{

 publicstatic void main(String[] args)

 {

 

    Tickett = new Ticket();

 

    Threadt1 = new Thread(t);

    Threadt2 = new Thread(t);

    t1.start();

    try{Thread.sleep(10);}catch(Exceptione){}

    t.flag= false;

    t2.start();

 

 

 }

}

 

 

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

4.         单例设计模式。

class Single

{

 privatestatic Single s = null;

 privateSingle(){}

 

 

 publicstatic Single getInstance()

 {

    if(s==null)

    {

       synchronized(Single.class)

       {

          if(s==null)

            s= new Single();

       }

    }

    returns;

 }

}

 

class SingleDemo

{

 publicstatic void main(String[] args)

 {

    System.out.println("HelloWorld!");

 }

}

2.         多线程死锁。

class Test implements Runnable

{

 privateboolean flag;

 Test(booleanflag)

 {

    this.flag= flag;

 }

 

 publicvoid run()

 {

    if(flag)

    {

       while(true)

       {//假如t1a锁并进入

          synchronized(MyLock.locka)

          {

            System.out.println(Thread.currentThread().getName()+"...iflocka ");//t1ab锁被t2拥有,只有t2执行完下面的语句才会释放b锁,所以t1停止这里。

            synchronized(MyLock.lockb)

            {

             System.out.println(Thread.currentThread().getName()+"..iflockb");          

            }

          }

       }

    }

    else

    {

       while(true)

       {//假如t1阻塞,t2执行,t2b锁进行

          synchronized(MyLock.lockb)

          {

 System.out.println(Thread.currentThread().getName()+"..elselockb");               同理t2也被挂在这里。因为t2没有a锁。

            synchronized(MyLock.locka)

            {

             System.out.println(Thread.currentThread().getName()+".....elselocka");

            }

          }

       }

    }

 }

}

 

 

class MyLock

{

 staticObject locka = new Object();

 staticObject lockb = new Object();

}

 

class DeadLockTest

{

 publicstatic void main(String[] args)

 {

    Threadt1 = new Thread(new Test(true));

    Threadt2 = new Thread(new Test(false));

    t1.start();

    t2.start();

 }

}

3.         *)线程间通信。

 当前的线程对象持有的锁对象调用:

wait():当前持有锁的线程对象处于了冻结状

notify():唤醒线程池中首个被冻结的线程。

notifyAll();唤醒线程池中所有的被冻结的线程。

 它们都使用在同步中,因为要对持有监视器()的线程操作。所以要使用在同步中,因为只有同步才具有锁。为什么这些操作线程的方法要定义Object类中呢?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

(因为要被当做锁的对象不确定,即所有对象都要有notifywait的功能,既然所有类型的对象都要具备这两个功能,那只能定义在Object(上帝)中了,因为所有类都继承自Object)。

1.         示例。


 

class Res

{

 Stringname;

 Stringsex;

 booleanflag = false;

}

 

class Input implements Runnable

{

 privateRes r ;

 Input(Resr)

 {

    this.r= r;

 }

 publicvoid run()//input中的run方法

 {

    intx = 0;

    while(true)

    {

       synchronized(r)

       {

 

          if(r.flag)

            try{r.wait();}catch(Exceptione){}

          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

{

 privateRes r ;

 

 Output(Resr)

 {

    this.r= r;

 }

 publicvoid run()

 {

    while(true)

    {

       synchronized(r)

       {

          if(!r.flag)

            try{r.wait();}catch(Exceptione){}

          System.out.println(r.name+"...."+r.sex);

          r.flag= false;

          r.notify();

       }

    }

 }

}

 

 

class InputOutputDemo

{

 publicstatic void main(String[] args)

 {

    Resr = new Res();

 

    Inputin = new Input(r);

    Outputout = new Output(r);

 

    Threadt1 = new Thread(in);

    Threadt2 = new Thread(out);

 

    t1.start();

    t2.start();

 }

}

以上代码的执行过程:

    1.创建一个公共数据类Res,创建两个实现Runnable接口的线程对象。

    2.创建两个线程对象,将对象传给创建线程的Thread对象用于执行run方法。

    3.启动线程t1.t2t1t2run方法不同,但两者操作的都是同一个对象的属性,即共享属性,但是操作的动作不同。这就被称为线程之间的通信。

    4.t1线程先被启动,执行in中的run方法。,第一次执行。二,第二次执行。

 publicvoid run()//input中的run方法

 {

    intx = 0;

    while(true)

    {//同步前提:1.多线程。2.锁相同。

       synchronized(r)//两者使用的是同一个锁

       {

 

       if(r.flag)//1.flag
= false;
不执行6.flag =true

 //.7执行冻结当前线程wait()会自动抛异常,所以需要处理。

          try{r.wait();}catch(Exceptione){}

          if(x==0)2.x=0执行改变namesex

          {

            r.name="mike";

            r.sex="man";

          }

          else

          {

            r.name="丽丽";

            r.sex= "女女女女女";

          }

          x= (x+1)%2;3.x=-1;

          r.flag= true;4.flag
= true;

          r.notify();5.唤醒output线程即t2线程开启。

       }

    }

t2线程被启动,执行out中的run方法。,第一次执行。二,第二次执行。

class Output implements Runnable

{

 privateRes r ;

 

 Output(Resr)

 {

    this.r= r;

 }

 publicvoid run()

 {

    while(true)

    {

       synchronized(r)

       {

//1.flag = true不执行。

       if(!r.flag)//
5.t2进入冻结状态。        try{r.wait();}catch(Exception e){}

//2.输出被in修改的namesex

          System.out.println(r.name+"...."+r.sex);

          r.flag= false;//3.flat =false;

          r.notify();//4.唤醒in线程,即t1线程。

       }

    }

 }

}

t1被唤醒时,t1又开始的上述步骤。当5t2又开始。

 

2.         解决安全问题。

3.         等待唤醒机制

4.         代码优化。

5.         (必须掌握)练习:生产者消费者。

          

 

 

class ProducerConsumerDemo

{

 publicstatic void main(String[] args)

 {

    Resourcer = new Resource();

 

    Producerpro = new Producer(r);

    Consumercon = new Consumer(r);

 

    Threadt1 = new Thread(pro);

    Threadt2 = new Thread(pro);

    Threadt3 = new Thread(con);

    Threadt4 = new Thread(con);

 

    t1.start();

    t2.start();

    t3.start();

    t4.start();

 

 }

}

 

/*

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

 

 

为什么定义notifyAll

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

 

*/

 

 

class Resource

{

 privateString name;

 privateint count = 1;

 privateboolean flag = false;

       // t1   t2

 publicsynchronized void set(String name)

 {

    while(flag)

       try{this.wait();}catch(Exceptione){}//t1(放弃资格) t2(获取资格)

    this.name= name+"--"+count++;

 

    System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);

    flag= true;

    this.notifyAll();

 }

 

 

 // t3  t4 

 publicsynchronized void out()

 {

    while(!flag)

       try{wait();}catch(Exceptione){}//t3(放弃资格) t4(放弃资格)

    System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);

    flag= false;

    this.notifyAll();

 }

}

 

class Producer implements Runnable

{

 privateResource res;

 

 Producer(Resourceres)

 {

    this.res= res;

 }

 publicvoid run()

 {

    while(true)

    {

       res.set("+商品+");

    }

 }

}

 

class Consumer implements Runnable

{

 privateResource res;

 

 Consumer(Resourceres)

 {

    this.res= res;

 }

 publicvoid run()

 {

    while(true)

    {

       res.out();

    }

 }

}



抱歉!评论已关闭.