黑马程序员——Java基础---多线程的概述

时间:2021-03-06 00:43:04
------<a href=" http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

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

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

一个进程至少有一个线程。

3.线程的状态:创建,运行,冻结,阻塞,消亡。

4.线程的一些API函数

static ThreadcurrentThread():获取当前线程对象

getName():获取线程名称

setName():设置线程名称或者构造函数

5.多线程的创建方式

1>继承Thread:

a>定义类继承Thread

b>复写Thread中的run()方法

c>调用线程的start方法:启动线程,调用run()方法

class Demo extends Thread //继承Thread

{

       //复写run方法

       public void run()

       {

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

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

       }

}

class ThreadDemo

{

       public static voidmain(String[] args)

       {

              Demo d = new Demo();

              d.start();//调用start方法,启动线程

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

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

       }

}

2>实现Runnable接口

a>定义类实现Runnable接口

b>覆盖Runnable接口中的run方法

c>通过Thread类建立线程对象

d>Runnable接口的子类对象作为实际参数传递给Thread类的构造方法

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

/*

 * 创建线程方式二:实现Runnable接口

 *

 

*/

class Ticket implements Runnable

{

       private int tick = 100;

       public void run()

       {

              while(true)

              {

                     if(tick > 0)

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

              }

       }

}

public class ThreadDemo2 {

 

       public static voidmain(String[] args) {

              // TODO Auto-generated method stub

              //创建实现Runnable接口子类Ticket对象

              Ticket t = new Ticket();

              Thread t1 = new Thread(t);//创建接收tThread对象t1

              Thread t2 = new Thread(t);//创建接收tThread对象t2

              t1.start();//开启线程

              t2.start();//开启线程

       }

 

}

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

a>实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。

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

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

6.多线程安全问题

1>产生安全原因:当一个线程在执行共享数据时,还未执行完,另一个线程参与进来,                           导致共享数据的错误。

  解决方法:每时每刻只让一个线程执行共享数据,当其未执行完,其它线程不可以参                    与执行。

2>解决多线程安全问题方法

a>同步代码块

  格式://对象即为同步监视器,也就是所谓的锁

//锁可以使用object的对象,如Object obj = new Object()

synchronized对象

    {

需要被同步的代码

}

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

class Bank

{

       private int sum;

       private Object obj = newObject();//定义同步代码块的锁

       public void add(int num)

       {

//同步代码块

              synchronized(obj)

              {

                     sum = sum + num;

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

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

              }

}

}

class Cus implements Runnable

{

       private Bank b = newBank();

       public void run()

       {

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

              {

                     b.add(100);

              }

       }

}

class BankDemo

{

       public static voidmain(String[] args)

       {

              Cus c = new Cus();

              Thread t1 = new Thread(c);

              Thread t2 = new Thread(c);

              t1.start();

              t2.start();

       }

}

同步前提:1)必须要有两个或者两个以上的线程。

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

同步代码块的利弊:

利:可以解决多线程的安全性

弊:多个线程都要判断锁,消耗资源

哪些代码同步?:明确哪些代码是多线程运行代码;明确共享数据;明确多线程运                               行代码中哪些语句是操作共享数据。

b>同步函数

格式:直接将synchronized放到函数上进行修饰

  例:public synchronized void add()

将上述代码中的Bank类按下更改,即可通过同步函数解决多线程访问安全

class Bank

{

       private int sum;

       public synchronized voidadd(int num)//同步函数

       {

              sum = sum + num;

              try{Thread.sleep(10);}catch(InterruptedException e){}

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

       }

}

同步函数使用的锁是:this

静态同步函数使用的锁是:类名.class(字节码文件对象)该对象的类型是class

7.线程间通信

1>线程间通信:其实就是多个线程在操作一个资源,但是操作的动作不同。

2>等待唤醒机制

a>wait,notify,notifyall都要使用在同步中(synchronized),因为要对持有监视器(锁)的线程操作。只有同步才具有锁。

waitsleep区别?

1wait可以指定时间也可以不指定。

  sleep必须指定时间。

2)在同步中时,对cpu的执行权和锁的处理不同。

wait:释放执行权,释放锁。

sleep:释放执行权,不释放锁。

等待和唤醒必须是同一个锁。

class Res

{

       private String name;

       private int count = 1;

       private boolean flag =false;//定义标志,用于进程间切换

       public synchronized voidset(String name)

       {

//使用while判断,让被唤醒的线程再一次判断

              while(flag)//if(flag)

                     try{this.wait();}catch(Exceptione){}//wait等待,锁是this

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

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

              flag = true;

              this.notifyAll();//唤醒全部线程//this.notify

       }

       public synchronized voidout()

       {

//使用while判断,让被唤醒的线程再一次判断

              while(!flag)//if(flag)

                     try{this.wait();}catch(Exceptione){}//wait等待,锁是this

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

              flag = false;

              this.notifyAll();//唤醒全部线程//this.notify

       }

}

 

class Producer implements Runnable

{

       private Res r;

       Producer(Res r)

       {

              this.r = r;

       }

       public void run()

       {

              while(true)

              {

                     r.set("商品");

              }

       }

}

 

class Consumer implements Runnable

{

       private Res r;

       Consumer(Res r)

       {

              this.r = r;

       }

       public void run()

       {

              while(true)

              {

                     r.out();

              }

       }

}

class ProducerConsumerDemo

{

       public static voidmain(String [] args)

       {

              Res r = new Res();

              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();

       }

}

b>Lock接口与Condition接口(也是JDk1.5的新特性)

Lock替代了synchronized;而Condition替代了Object中的监视器方法(wait,notify,notifyall)使用,Condition可以通过Lock锁获取,一个Lock可以对应多个Condition

与之对应上面代码,我们只要更改Res类中代码如下:

class Res

{

       private String name;

       private int count = 1;

       private boolean flag =false;//定义标志,用于进程间切换

       private Lock lock = newReentrantLock();

       private Condition con =lock.newCondition();

//private Condition con_pro =lock.newCondition();

//private Condition con_con =lock.newCondition();

//此处抛了异常,在处理线程时一定要记得做异常处理

       public void set(Stringname)throws InterruptedException

       {

              lock.lock();//上锁,lock()unlock()其实就类似于synchronized

              try{

                     while(flag)

                            con.await();//线程等待,类似于wait

//con_pro.await();

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

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

                     flag = true;

                            con.signalAll();//唤醒线程,类似于notifyAll

//con_con.signal();

              }

              finally{

                     lock.unlock();//释放锁

              }

             

       }

//此处抛了异常,在处理线程时一定要记得做异常处理

       public void out()throwsInterruptedException

       {

              lock.lock();//上锁

              try{

                     while(!flag)

                            con.await();//线程等待

//con_con.await();

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

                     flag = false;

                     con.signalAll();//唤醒线程

//con_pro.signal();

              }

              finally{

                     lock.unlock();//释放锁

              }

       }

}

//以上注释的代码,实现了等待本方线程和只唤醒对方线程的功能。

8.停止线程

stop方法已经过时,run方法结束才可以停止线程

1>定义循环结束标记

因为线程运行的代码一般都是循环,只要控制了循环即可。

2>使用interrupt(中断)方法

该方法是结束线程的冻结状态,使线程回到运行状态中来。

9.join:抢夺cpu执行权

A线程执行到B线程的.join()方法是,A就会等待。等待B线程执行完后A才会执行。

join可以用来临时加入线程执行。