java--多线程(2)--黑马程序员

多线程(2)

主要内容:《 线程组、线程池、JDK5的线程实现方式、 定时器、wait_sleep_yield的在同步方法内的区别、设计模式、单例模式之饿汉式、懒汉式、Runtime类》

1.线程组

我们可以将一些线程进行"分组",分组的目的是可以方便管理,统一进行一些操作;
 
  一.默认线程组:
      所有线程,默认都属于一个线程组:main
  二.我们可以将一些线程放到我们指定一个"线程组"内;
     1).ThreadGroup:它表示一个线程组;
     2).实例化我们的线程对象;
     3).实例化一个Thread,使用:自定义线程组、线程、线程名称做参数;
  
 三.我们可以对一个线程组内的所有线程进行统一操作,比如:停止一个线程组内的所有线程:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         MyThread t1 = new MyThread();
 4         MyThread t2 = new MyThread();
 5         
 6         ThreadGroup g1 = t1.getThreadGroup();
 7         ThreadGroup g2 = t2.getThreadGroup();
 8         //一.默认情况下,两个线程都属于main线程组;
 9         System.out.println("t1的线程组:" + g1.getName());
10         System.out.println("t2的线程组:" + g2.getName());
11         
12         //二.自定义线程组
13         ThreadGroup tg = new ThreadGroup("我的线程组");
14         MyThread t3 = new MyThread();
15         MyThread t4 = new MyThread();
16         
17         Thread thread1 = new Thread(tg,t3,"线程1");
18         Thread thread2 = new Thread(tg,t4,"线程2");
19         
20         System.out.println("thread1的线程组:" + thread1.getThreadGroup().getName());
21         System.out.println("thread2的线程组:" + thread2.getThreadGroup().getName());
22         
23         thread1.start();
24         thread2.start();
25         
26         try {
27             System.out.println("主进程睡眠两秒......");
28             Thread.sleep(1000 * 2);
29         } catch (InterruptedException e) {
30             // TODO Auto-generated catch block
31             e.printStackTrace();
32         }
33         //三.一次性停掉t3和t4
34         System.out.println("主进程醒来,一次性停掉tg内的所有线程......");
35         tg.interrupt();
36         
37         
38     }
39 }
40 
41 public class MyThread extends Thread{
42     @Override
43     public void run() {
44         for(int i = 0; i < 100;i++){
45             try {
46                 Thread.sleep(500);
47                 System.out.println(this.getName() + " i = " + i);
48             } catch (InterruptedException e) {
49                 System.out.println(this.getName() + " 线程被终止......");
50                 break;
51             }
52         }
53     }
54 }

2.线程池

  1.我们知道,对于同一个Thread对象,不能重复调用start()方法;
  2.如果我们再想使用,只能重新实例化一个新线程对象;如果这种实例化比较耗时,这样效率就很低;
  3.这种情况下,可以使用"线程池":
    1).它可以缓存一些已经执行过的线程;
    2).对于存在于缓存池中的线程,可以直接执行;
    3).在JDK5之后,提供了"线程池"的概念:
  Executors(类):
  public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
  public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池,
  public static ExecutorService newSingleThreadExecutor():创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。新创建的单线程     Executor
返回值:ExecutorService(类):
Future<?> submit(Runnable task):执行并缓存线程对象;

停止线程:
shutdown();

 1 public class Demo {
 2     public static void main(String[] args) {
 3         //**********不使用线程池******************************//
 4         /*MyThread t1 = new MyThread();//耗时
 5         t1.start();
 6         
 7         try {
 8             Thread.sleep(1000 * 2);
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12         //后续代码中,我们还想再次运行一次t1
13     //    t1.start();//java.lang.IllegalThreadStateException
14         
15         MyThread t2 = new MyThread();//也会耗时5秒;
16         t2.start();*/
17         //*****************************************************//
18         //使用线程池;
19         ExecutorService service = Executors.newFixedThreadPool(2);
20         
21         MyThread t4 = new MyThread();
22         MyThread t5 = new MyThread();
23         service.submit(t4);//执行t4并缓存t4
24         service.submit(t5);//执行并缓存t5
25         
26         try {
27             Thread.sleep(1000);//等待1秒,为了确保上述两个线程执行完毕;
28         } catch (InterruptedException e) {
29             e.printStackTrace();
30         }
31         
32         //如果后续还需要执行t4和t5
33         System.out.println("******************再次运行t4和t5*****************************");
34         service.submit(t4);//不会再执行构造方法
35         service.submit(t5);//不会再执行构造方法
36         
37         service.shutdown();
38         
39         
40         
41     }
42 }
 1 public class MyThread extends Thread {
 2     public MyThread(){
 3         System.out.println("构造一个我很耗时噢,需要5秒......");
 4         for(int i = 1 ;i <= 5 ;i++){
 5             System.out.println("第:" + i + " 秒");
 6             try {
 7                 Thread.sleep(1000);
 8             } catch (InterruptedException e) {
 9             }
10         }
11         System.out.println("构造完毕!");
12     }
13     @Override
14     public void run() {
15         for(int i = 0 ;i < 100; i++){
16             System.out.println(this.getName() + "  i = " + i);
17         }
18     }
19 }

3.JDK5的线程实现方式

JDK5的实现线程的方式:
 
  步骤:
  1.实现Callable接口;
  2.重写:call()方法;
  3.启动线程:
    1.获取线程池:
      三种方式:
      Executors(类):
      public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
      public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池,
      public static ExecutorService newSingleThreadExecutor():创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。新创建      的单线程     Executor
  2.调用线程池的
    ExecutorService(类):
    Future<?> submit(Callable task):执行并缓存线程对象;

 1 public class Demo {
 2     public static void main(String[] args) {
 3         //1.获取一个线程池
 4         ExecutorService service = Executors.newFixedThreadPool(2);
 5         //2.调用线程池的submit()方法
 6         service.submit(new MyCallable());
 7         //3.关闭线程池
 8         service.shutdown();
 9     }
10 }
11 
12 public class MyCallable implements Callable {
13 
14     @Override
15     public Object call() throws Exception {
16         for(int i = 0;i < 100; i++){
17             System.out.println("i = " + i);
18         }
19         return null;
20     }
21 
22 }

4.JDK5的线程实现方式接收返回值

 1 import java.util.concurrent.ExecutionException;
 2 import java.util.concurrent.ExecutorService;
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.Future;
 5 /*
 6  * 之前我们使用Thread或Runnable中的run()是不能接收返回值的,但
 7  * JDK5的线程实现方式,可以接收返回值了。
 8  */
 9 public class Demo {
10     public static void main(String[] args) {
11         //1.获取一个线程池
12         ExecutorService service = Executors.newFixedThreadPool(2);
13         //2.调用线程池的submit()方法
14         //Future<?> submit(Callable task)
15         Future<Integer> result = service.submit(new MyCallable());
16         try {
17             System.out.println("线程计算的结果是:" + result.get());
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         } catch (ExecutionException e) {
21             e.printStackTrace();
22         }
23         //3.关闭
24         service.shutdown();
25     }
26 }
 1 import java.util.concurrent.Callable;
 2 
 3 public class MyCallable implements Callable<Integer> {
 4 
 5     //计算1--100的累加和,并将结果返回
 6     @Override
 7     public Integer call() throws Exception {
 8         int sum = 0;
 9         for(int i = 1 ;i <= 100;i++){
10             sum += i;
11         }
12         return sum;
13     }
14 
15 }

5.匿名内部类的方式实现多线程程序

匿名内部类的方式实现多线程程序

  1.new Thread(){匿名的Thread的子类};
  2.new Thread(匿名的Runnable的子类){};
  3.new Thread(匿名的Runnable的子类){匿名的Thread的子类};

 1 public class Demo {
 2     public static void main(String[] args) {
 3         //1.new Thread(){匿名的Thread的子类};
 4         new Thread(){
 5             //重写run()
 6             @Override
 7             public void run() {
 8                 System.out.println("a");
 9             }
10         }.start();
11         
12         //2.new Thread(匿名的Runnable的子类){};
13         new Thread(new Runnable(){
14             @Override
15             public void run() {
16                 System.out.println("b");
17             }
18         }){}.start();
19         
20         //3.new Thread(匿名的Runnable的子类){匿名的Thread的子类};
21         new Thread(new Runnable(){
22             @Override
23             public void run() {
24                 System.out.println("c");
25             }
26         }){
27             @Override
28             public void run() {
29                 System.out.println("d");//最终执行的是子类的run()
30             }
31         }.start();
32     }
33 }

6.定时器

定时器:

  1.在指定的时间,完成指定的任务;
  2.在指定的时间开始,每个多长时间,重复的执行指定的任务;
 
  Java中实现定时器:
  1.java.util.TimerTask(抽象类):描述具体任务:
    1).定义类,继承自:TimerTask;
    2).重写run()方法。将具体的任务,写到这里;
  2.java.util.Timer类:启动TimerTask的:
   1).构造一个Timer对象;使用无参构造方法;
   2). public void schedule(TimerTask task, long delay):在指定的delay时间后,执行task中的任务;
  public void schedule(TimerTask task,long delay,long period)在指定的delay时间后,执行task中的任务;并且每隔period时间后,会反复的执行;
  

 1 class MyTimerTask extends TimerTask{
 2     private Timer timer;
 3     public MyTimerTask(Timer t){
 4         this.timer = t;
 5     }
 6     @Override
 7     public void run() {
 8         /*for(int i = 0;i < 100; i++){
 9             System.out.println("i = " + i);
10         }*/
11         System.out.println("duang......");
12     //    this.timer.cancel();//如果此任务被反复执行,就不需要cancel()了;
13     }
14 }
15 public class Demo {
16     public static void main(String[] args) {
17         Timer timer = new Timer();
18         System.out.println("5秒后开始执行任务......");
19     //    timer.schedule(new MyTimerTask(timer), 1000 * 5);
20         timer.schedule(new MyTimerTask(timer), 1000 * 5,1000 );//每隔1秒执行一次;
21     //    timer.cancel();//终止任务。此方法要在TimerTask中调用,保证任务执行完毕;
22     }
23 }

7.wait_sleep_yield的在同步方法内的区别

 1.wati():释放锁;Object类的;可以指定时间。使用notify()或notifyAll()唤醒,或者"指定的时间"到了,会自动唤醒;
 2.sleep():不释放锁;Thread类:必须指定时间。当休眠时间到,会自动唤醒;
 3.yield():不释放锁;Thread类;是当前访问的线程退回到就绪状态。但是,当在"同步"方法中使用时,不会释放锁;
 

 1 public class Demo {
 2     public static void main(String[] args) throws InterruptedException {
 3         //1.实例化一个Accound
 4         Accound acc = new Accound();
 5         //2.实例化两个线程
 6         MyThread t1 = new MyThread(acc);
 7         MyThread t2 = new MyThread(acc);
 8         
 9         t1.setName("刘亦菲");
10         t2.setName("周星驰");
11         
12         t1.start();
13         t2.start();
14         
15         //唤醒。用来测试wait()
16         /*for(int i = 0;i < 10 ;i++){
17             Thread.sleep(2);
18             acc.show2();//唤醒Accound对象中等待的线程;
19             
20         }*/
21         
22     }
23 }
 1 public class Accound {
 2     public synchronized void show(String threadName){
 3         //************wait()方法**************
 4         /*
 5         try {
 6             System.out.println(threadName + " 开始等待......");
 7             this.wait();
 8             System.out.println(threadName + " 醒来!");
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12         */
13         
14         //************sleep()方法***************
15         /*try {
16             System.out.println(threadName + " 开始睡眠2秒......");
17             Thread.sleep(1000 * 2);
18             System.out.println(threadName + " 睡眠醒来!");
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }*/
22         
23         //***********yield()方法*****************
24         System.out.println(threadName + " 礼让......");
25         Thread.yield();
26         System.out.println(threadName + " 礼让结束!");
27     }
28     //此方法用来测试wait()的
29     public synchronized void show2(){
30         this.notifyAll();
31     }
32 }
 1 public class MyThread extends Thread{
 2     private Accound acc;
 3     public MyThread(Accound acc){
 4         this.acc = acc;
 5     }
 6     @Override
 7     public void run() {
 8         for(int i = 0;i < 10;i++){
 9             this.acc.show(this.getName());
10         }
11     }
12 }

8.设计模式的概念

  1.模式:人们在长期的生产经营活动中,会反复的遇到一些问题,并使用一些固定的方法去解决这些问题。这种反复的遇到问题并
   使用某种方式去解决问题的形式,就叫:模式;
  2.Java与模式:以前的程序员在长期的使用Java语言开发过程中,会遇到各种问题,并使用某些手段去解决这些问题,这些手段通常是利用
   Java语言的一些特性:继承、封装、多态.....去解决问题;长期以来,就形成了一种固定的方式:就是使用Java语言解决
   某种问题的方案。

  3.Java中设计模式的分类:
  创建型模式
  行为型模式
  结构型模式

9.简单工厂模式概述和使用

我们以前获取某些对象,都是直接实例化。
 这种方式,将实例化对象与前端的代码混杂在一起。
  工厂模式:
  1.对于某些对象的实例化,先建立某些"工厂类";
  2.前端获取对象,不用直接实例化,通过工厂类的某些方法来获取;
  3.特点:
   1).前端使用对象,不需要自己实例化,通过"工厂类"去获取;
   2).如果新增一个产品,需要修改"工厂类";

 1 public class Demo {
 2     public static void main(String[] args) {
 3         //之前的方式,直接实例化对象;
 4         Cat c = new Cat();
 5         Dog d = new Dog();
 6         //使用工厂;
 7         //方式一:
 8         Cat c2 = AnimalFactory.getCat();
 9         Dog d2 = AnimalFactory.getDog();
10         
11         //方式二:
12         Cat c3 = (Cat)AnimalFactory.getAnimal("猫");
13         Dog d3 = (Dog)AnimalFactory.getAnimal("狗");
14         
15     }
16 }
17 public class AnimalFactory {
18     //*********实现方式一:针对某一种类,都提供一个方法,获取此类对象**********
19     public static Cat getCat(){
20         return new Cat();
21     }
22     public static Dog getDog(){
23         return new Dog();
24     }
25     
26     //*********实现方式二:只提供一个方法,根据参数来判断是获取哪个对象********
27     public static Object getAnimal(String type){
28         if(type.equals("猫")){
29             return new Cat();
30         }
31         if(type.equals("狗")){
32             return new Dog();
33         }
34         return null;
35     }
36 }
37 
38 
39 public class AnimalFactory {
40     //*********实现方式一:针对某一种类,都提供一个方法,获取此类对象**********
41     public static Cat getCat(){
42         return new Cat();
43     }
44     public static Dog getDog(){
45         return new Dog();
46     }
47     
48     //*********实现方式二:只提供一个方法,根据参数来判断是获取哪个对象********
49     public static Object getAnimal(String type){
50         if(type.equals("猫")){
51             return new Cat();
52         }
53         if(type.equals("狗")){
54             return new Dog();
55         }
56         return null;
57     }
58 }
59 public class Cat {
60 
61 }
62 
63 public class Dogt {
64 
65 }

10.工厂方法模式的概述和使用


  工厂方法模式的概述和使用
 
  之前的 "简单工厂模式"如果新增一个产品的话,需要修改"工厂类"。
  此模式,可以达到当新增产品时,无需修改任何类;
 
  步骤:
  1.建立两个接口:
    1).产品接口:IAnimal
    2).工厂接口:IFactory
  2.建立具体的产品类,和具体的工厂类;
    1).具体产品:Cat implements IAnimal
    2).具体工厂类:CatFactory implements IFactory
 
  好处:如果新增产品时,无需对原来的任何类进行修改;
  弊端:类比较多;

 1 public class Demo {
 2     public static void main(String[] args) {
 3         CatFactory catFactory = new CatFactory();
 4         Cat c = (Cat) catFactory.getAnimal();
 5     }
 6 }
 7 
 8 public class CatFactory implements IFactory {
 9 
10     @Override
11     public  IAnimal getAnimal() {
12         return new Cat();
13     }
14 
15 }
16 
17 
18 public interface IAnimal {
19 
20 }
21 
22 interface IFactory {
23     public IAnimal getAnimal();
24 }
25 
26 public class Cat implements IAnimal {
27 
28 }

11.单例模式之饿汉式

单例模式之饿汉式
 
  1.单例模式:
  单:一个;
  例:实例--对象;
  2.在我们的程序运行期间,有些类的对象,只需要一个对象,这种情况下,就得需要将此类设计为:单例模式;
  3.设计单例模式:例如:将Student设计为单例模式;
  步骤:
  1.任何类不能实例化Student类的对象:将Student的构造方法私有化;
  2.Student内部持有一个自己对象的引用,(饿汉式--先实例化)
  3.提供一个公有方法,为外部获取此引用;

 1 public class Demo {
 2     public static void main(String[] args) {
 3         Student stu = Student.getStudent();
 4         Student stu2 = Student.getStudent();
 5         Student stu3 = Student.getStudent();
 6         System.out.println("stu == stu2 : " + (stu == stu2));
 7         System.out.println("stu == stu3 : " + (stu == stu3));
 8     }
 9 }
10 
11 public class Student {
12     private static Student stu = new Student();
13     private Student(){
14         
15     }
16     //为了封装,将成员变量私有化,提供公有方法
17     public static Student getStudent(){
18         return stu;
19     }
20 }

12.单例模式--懒汉式

1.在Student内的成员变量,先期不实例化;
2.在公有方法内,先判断是否为null,如果为null再实例化;
3.为了Student类的安全,公有方法可能会被多线程并发访问,为了安全,将
  此方法声明为同步的。

 1 public class Demo {
 2     public static void main(String[] args) {
 3         Student stu1 = Student.getStudent();//第一次使用getStudent()方法时,会new一个Student对象;
 4         Student stu2 = Student.getStudent();//第二次调用getStudent()方法时,会直接返回一个Student引用;
 5         Student stu3 = Student.getStudent();
 6         
 7         System.out.println("stu1 == stu2 : " + (stu1 == stu2));
 8         System.out.println("stu1 == stu3 : " + (stu1 == stu3));
 9     }
10 }
11 
12 public class Student {
13     private static Student stu ;
14     private Student(){
15         
16     }
17     //为了封装,将成员变量私有化,提供公有方法
18     public synchronized static Student getStudent(){
19         if(stu == null){
20             stu = new Student();
21         }
22         return stu;
23     }
24 }

13.类库中的Runtime类

java.lang.Runtime:

 每个 Java 应用程序都有"一个" Runtime 类实例.使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前Runtime对象。
 内部:单例模式饿汉式实现;

 

public class Demo {
    public static void main(String[] args) {
        Runtime time = Runtime.getRuntime();
        try {
            
            time.exec("notepad");
            time.exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

posted on 2015-08-06 21:40  林木森  阅读(165)  评论(0编辑  收藏  举报

导航