黑马程序员——第七章 多线程

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


进程和线程

进程:就是应用程序在运行时期,所占用的内存空间区域,这个区域就称为这个应用程序的进程。

线程:是程序执行的一条路径,一个进程中可以包含多条线程。

多线程并发执行可以提高程序的效率,可以同时完成多项工作。

 

线程创建方式

方式一:继承Thread类

 

1, 定义类继承Thread类。

2, 重写run方法,把新线程要做的事写在run方法中。

3, 创建线程对象,调用Thread类的start()方法开启新线程, 内部会自动执行run方法。

例:public class ThreadDemo {

  public static voidmain(String[] args) {

// 创建线程对象

         ThreadSub ts = new ThreadSub();

// start()方法开启新线程

         ts.start();

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

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

  }

}

// 定义类ThreadSub继承Thread类

class ThreadSub extends Thread {

// 重写run方法

  public void run() {

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

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

  }

}

 

方式二:实现Runnable接口

 

1,  定义类实现Runnable接口。

2,  实现run方法,把新线程要做的事写在run方法中。

3,  创建Thread对象,构造方法传入Runnable接口实现类对象。

4,  调用Thread类的start()开启新线程,内部会自动调用Runnable的run()方法。

例:public class RunnableDemo {

      public static voidmain(String[] args) {

             // 创建Runnable实现类对象

             RunnableSub rs = new RunnableSub();

             // Thread对象构造方法传入Runnable实现类对象,start()方法开启新线程

             new Thread(rs).start();

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

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

      }

}

// 定义类RunnableSub实现Runnable接口

class RunnableSub implements Runnable {

      // 重写run方法

      public void run() {

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

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

      }

}

 

用匿名内部类实现两种线程的方式

继承Thread类

new Thread() {//new 类(){}继承这个类

   public void run() {//重写run方法

       for(int i = 0; i < 3000; i++) {//将要执行的代码,写在run方法中

           System.out.println("aaaaaaaaaaaaaaaaaaaaaaaa");

       }

    }

}.start();

 

实现Runnable接口

new Thread(new Runnable(){//new 接口(){}实现这个接口

   public void run() {//重写run方法

       for(int i = 0; i < 3000; i++) {//将要执行的代码,写在run方法中

           System.out.println("bb");

       }

    }

}).start();

 

两种方式对比:

继承Thread类:单继承,代码简单。

实现Runnable接口:避免了单继承局限性,代码复杂,推荐使用。

 

线程之间的同步

由于多个线程访问出现延迟和线程随机性,所以多线程不安全,需要同步保证安全。

 

同步代码块

使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块。多个同步代码块如果使用相同的锁对象,那么他们就是同步的。

格式:synchronized(对象)

{

  需要同步的代码;

}

同步可以解决安全问题的根本原因就在那个对象上。该对象有如同锁的功能。

同步代码块中,只能有一个线程运行,保证了安全,但牺牲了运行速度。

 

同步方法

使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的。

 

死锁问题

多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁,尽量不要嵌套使用。

例:public class DeadlockDemo {

      public static void main(String[]args) {

             // 创建Runnable接口实现类对象,传递标签值

             Dead d = new Dead(true);

             Dead d1 = new Dead(false);

             // 创建两个线程

             new Thread(d).start();

             new Thread(d1).start();

      }

}

// 定义锁locka

class LockA {

      public static final LockAlocka = new LockA();

}

// 定义锁lockb

class LockB {

      public static final LockBlockb = new LockB();

}

// 定义类Dead实现Runnable

class Dead implements Runnable {

      // 定义一个标签

      private boolean flag;

 

      Dead(boolean flag) {

             this.flag = flag;

      }

      public void run() {

             while (true) {

                    if (flag == true){

                           // 进入A房间,同步代码块,获取锁locka

                           synchronized (LockA.locka) {

                                  System.out.println("if...locka");

                                  //进入B房间,同步代码块,获取锁lockb

                                  synchronized(LockB.lockb) {

                                         System.out.println("if...lockb");

                                  }

                           }

                    } else {

                           // 进入B房间,同步代码块,获取锁lockb

                           synchronized (LockB.lockb) {

                                  System.out.println("else...lockb");

                                  //进入A房间,同步代码块,获取锁locka

                                  synchronized(LockA.locka) {

                                         System.out.println("else...locka");

                                  }

                           }

                    }

             }

      }

}

 

线程之间的通信

1,什么时候需要通信

多个线程并发执行时,在默认情况下CPU是随机切换线程的,如果我们希望他们有规律的执行, 就可以使用通信,例如每个线程执行一次打印。

2,怎么通信

如果希望线程等待,就调用wait( )

如果希望唤醒等待的线程,就调用notify( )

这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。

3,多个线程通信的问题

notify()方法是随机唤醒一个线程。

notifyAll()方法是唤醒所有线程。

JDK5之前无法唤醒指定的一个线程。

如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件。

例:// 定义产品资源对象

class Product{

       // 定义名字

       private String name;

       // 定义计数器

       private int count ;

       // 定义标记

       private boolean flag = true;

       // 生产方法,是让生产线程调用

       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 = false;

              this.notifyAll();

       }

       // 消费方法,是让消费线程调用

       public synchronized void get(){

              while(flag)

                     try{this.wait();}catch(Exception e){}

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

              flag = true;

              this.notifyAll();

       }

}

// 生产者线程

class Produce implementsRunnable{

       private Product p;

       Produce(Product p) {this.p = p;}

       public void run(){

              while(true)

                     p.set("Iphone");

       }

}

// 消费者线程

class Consumerimplements Runnable{

       private Product p ;

       Consumer(Product p){this.p = p;}

       public void run(){

              while(true)

                     p.get();

       }

}

public class ThreadDemo2{

       public static void main(String[] args) {

              Product p = newProduct();

              new Thread(new Produce(p)).start();

              new Thread(newProduce(p)).start();

              new Thread(newProduce(p)).start();

              new Thread(newProduce(p)).start();

              new Thread(newConsumer(p)).start();

              new Thread(newConsumer(p)).start();

              new Thread(newConsumer(p)).start();

              new Thread(newConsumer(p)).start();

       }

}

 

Thread类常用方法

1,获取名字,通过getName()方法获取线程对象的名字,返回String

2,设置名字:通过构造函数可以传入String类型的名字。

通过setName(String)方法可以设置线程对象的名字。

3,获取当前线程对象,Thread.currentThread(),主线程也可以获取。

4,休眠,Thread.sleep(毫秒), 控制当前线程休眠若干毫秒。

5,守护,setDaemon(),设置一个线程为守护线程, 该线程不会单独执行,当其他非守护线程都执行结束后,自动退出,必须写在start之前。

6,join(),等待该线程终止,使用了join方法的线程,会一直运行结束,其他线程再抢CPU的资源。

7,toString(),打印线程对象,调用线程的toString方法

 例:Thread[Thread-0,5,main]

 []里面的信息:Thread-0是线程的名字,5是线程的优先级,main是所属组,主线程组。

8,setPriority(int newPriority)设置线程的优先级。

优先级,Java中的线程,有三种优先级,最高,默认,最低,Thread类中用三个静态常量表示线程的优先级,优先级越高,线程在某一时刻运行的更多些。

MAX_PRIORITY =10 

NORM_PRIORITY =5   

MIN_PRIORITY =1

9,static yield(),线程的让步,该方法要写在run()中,出现线程之间相互谦让,不肯运行的效果。

10,方法stop(),停止线程运行,已被interrupt()替代。

11,interrupt(),停止线程运行,抛InterruptedException线程中断异常。

 

wait,notify,线程无限等待和唤醒等待线程,写在同步中,必须有锁的支持,锁为Object类保证任意对象锁都可以调用线程,等待唤醒方法。

 

wait和 sleep的区别

wait是Object类的非静态方法,必须有锁。

sleep是Thread类的静态方法,任意程序都可调用,无需锁。

sleep不会释放锁,wait线程释放同步锁。

 

JDK1.5之后的线程控制

同步

接口Lock,替代了原有的synchronized的使用,使用灵活广泛。

Lock实现类ReentrantLock的lock()和unlock()方法进行同步。

通信

使用Lock接口实现类ReentrantLock的newCondition()方法可以获取Condition对象。

需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法。

不同的线程使用不同的Condition,这样就能区分唤醒的时候找哪个线程了。

 

新旧方法的对比

   接口中Condition     Object类

      await()           wait()

      signal()           notify()

      signalAll()        notifyAll()

 

Timer类

构造方法中传递参数false

 Timer类的方法schedule(TimerTask task, Date firstTime, long period)定时器要运行的程序,开始时间,间隔时间。

第一个参数需要运行的程序,第二个参数是开始时间,第三个参数间隔的毫秒值。

   第一个参数是TimerTask类型,是一个抽象类,子类继承抽象类,重写run方法,传递子类的对象。

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值