深入了解java线程组

一. java线程组特点:

1.所有用户创建的线程默认属于main线程组。main线程组是JVM在应用程序运行时自动创建。

2.如果线程A创建了线程B,创建时未指定线程组,那么线程组B自动加入线程A所在线程组。

3.线程一旦加入某个线程组,该线程就一直留着该组中,直至线程死亡,中途不可改变所属线程组。

4.用户创建的线程组默认都有一个父线程组。如线程A创建了线程组X,那么线程A所在线程组则默认称为线程组X的父线程组。

通过以下示例可验证:

package net.mosang.threadgroup;
/**
 * 线程类
 * @author Heiry
 * 模拟登录
 */
public class SignUp implements Runnable{
  private String userName;
  private String passWord;
  
  public SignUp(String userName, String passWord) {
    this.userName = userName;
    this.passWord = passWord;
  }
  @Override
  public void run() {
    System.out.println("你登录的用户名是:"+userName+"密码是:"+passWord);
    if(userName.equals("创建线程组")) {
      ThreadGroup tg = new ThreadGroup("newTg");
      System.out.println("新线程内创建的线程组名是:"+tg.getName());
      System.out.println("新线程内创建的线程组父线程组名是:"+tg.getParent().getName());
    }		
  }
}
package net.mosang.threadgroup;
/**
 * 线程组示例
 * @author Heiry
 *
 */
public class ThreadGroupDemo {
  public static void main(String[] args) {	
    SignUp u1 = new SignUp("heiry", "123");
    SignUp u2 = new SignUp("mosang", "456");
    Thread tu1 = new Thread(u1);//创建线程
    Thread tu2 = new Thread(u2);//创建线程
    System.out.println(tu1.getThreadGroup().getName());//输出“main”,tu1所属线程组
    System.out.println(tu2.getThreadGroup().getName());//输出“main”, tu2所属线程组
    ThreadGroup g1 = new ThreadGroup("用户线程组A");//创建线组,不指定父线程组
    ThreadGroup g2 = new ThreadGroup("用户线程组B");//创建线组,不指定父线程组
    Thread tu3 = new Thread(g1,u1);//创建线程,指定线程组
    Thread tu4 = new Thread(g2,u2);//创建线程,指定线程组
    System.out.println(tu3.getThreadGroup().getName());//输出“用户线程组A”,tu3所属线程组
    System.out.println(tu4.getThreadGroup().getName());//输出“用户线程组B”, tu4所属线程组
    System.out.println("g1父线程组是--->"+g1.getParent().getName()); // 输出“main”
    System.out.println("g2父线程组是--->"+g2.getParent().getName()); // 输出“main”
    SignUp u3 = new SignUp("创建线程组", "789");//线程内将创建线程组
    Thread tu5 = new Thread(g1,u3);//创建线程,指定线程组
    tu5.start();
  }
}

输出结果:

main
main
用户线程组A
用户线程组B
g1父线程组是--->main
g2父线程组是--->main
你登录的用户名是:创建线程组密码是:789
新线程内创建的线程组名是:newTg
新线程内创建的线程组父线程组名是:用户线程组A

Callable通过线程池实现多线程

Callable 接口与Runnable接口类似可实现多线程的创建,但与Runnable不同的是,其线程方法call()有返回值并可抛出异常(Runnable 不会返回结果,并且无法抛出经过检查的异常)。另外Callable创建多线程依赖于线程池。

 

通过API查询,可以看到,Callable<V>是带泛型的,其泛型就是call方法返回值类型。

以下通过创建商品“秒杀”多线程FlashSale,实例讲解Callable 实现多线程的步骤:

1. 创建线程类FlashSale,实现Callable接口,重写call方法。

2.创建FlashSale线程实例binge,craze。

3.创建线程池对象实例shoppingPool

4.通过线程池实例shoppingPool的submit方法执行binge,craze。

package net.mosang.callabledemo;
import java.util.concurrent.Callable;
/**
 * FlashSale线程类
 * @author Heiry
 * 重写不带任何参数的 call方法,返回String类型的抢购结果提示
 */

public class FlashSale implements Callable<String>{
  private String goodsName;
  @Override
  public String call() throws Exception {
    if(goodsName.equals("Mac电脑")) {
      return "抢到了苹果电脑";
    }else if (goodsName.equals("Mi手机")) {
      return "抢到了小米手机";
    }
    else {
      return "什么也没抢到";
    }
  }
  public FlashSale(String goodsName) {
    super();
    this.goodsName = goodsName;
  }
}
package net.mosang.callabledemo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
 * 
 * @author Heiry
 *
 */
public class Shopping {
public static void main(String[] args) throws InterruptedException, ExecutionException {
  FlashSale binge = new FlashSale("Mac电脑"); //创建线程实例binge
  FlashSale craze = new FlashSale("Mi手机");//创建线程实例craze
  ExecutorService shoppingPool = Executors.newFixedThreadPool(2);
   Future<String> s1 = shoppingPool.submit(binge);
   Future<String> s2 = shoppingPool.submit(craze);
   System.out.println(s1.get());
   System.out.println(s2.get());
}
}

输出结果

抢到了苹果电脑
抢到了小米手机

可见,相比继承Thread与实现Runable接口方式实现多线程,Callable更方便地得到多线程的返回值并进行异常处理。另外,Callable依赖于线程池,通过此方式创建多线程,步骤繁琐,更适合处理高并发。

设计模式剖析系列之适配器模式

适配器模式包含以下主要角色。

1. 已经存在的、具备完备功能的类 Adaptee(适配者)。

2. 当前系统业务所期待的接口(Target),但与现Adaptee不兼容,它可以是抽象类或接口。

3. 适配器类(Adapter)。通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

实例:

现有一个两孔插座,能对外提供220v稳定电压,但现业务需要,需要提供三孔插座,同样输出220v稳定电压,这就需要一个适配器来进行转换。

代码结构及详细:

package net.mosang.adapter;

public interface ThreeSocket {
  public void threeSocket220v();//期望三孔输出220v电压的插线板
}
package net.mosang.adapter;

public class TwoSocket {
  public void twoSocket220v() {//现有两孔可输出220v电压的插线板
    System.out.println("220v电压持续输出中。。。。。");
  }
}
package net.mosang.adapter;

public class Adaptor implements ThreeSocket {
  private TwoSocket two; 
  @Override
  public void threeSocket220v() { //对外提供了三孔220v电压功能线板
    two.twoSocket220v();//实际依然是经过两孔输出
  }
  public TwoSocket getTwo() {
    return two;
  }
  public void setTwo(TwoSocket two) {
    this.two = two;
  }
  public Adaptor(TwoSocket two) {
    super();
    this.two = two;
  }
  
}
package net.mosang.adapter;

public class Client {

  public static void main(String[] args) {
    TwoSocket two = new TwoSocket();
    Adaptor adaptor = new Adaptor(two);
    adaptor.threeSocket220v(); // 输出"220v电压持续输出中。。。。。"
  }
}

UML关系图:

适配器模式示意图

设计模式剖析系列之原型模式

用一个已经创建的实例作为原型,克隆(clone)该原型对象来创建一个和原型相同或相似的新对象,免去创建对象的复杂过程和细节。原型模式分浅拷贝与深拷贝两种。

浅拷贝实现方式:

1.原型类实现CLoneable接口

2.拷贝对象通过原型的clone方法获得新的实例

深拷贝实现方式:

1.原型类实现CLoneable接口

2.原型类重写clone方法,并在克隆对象同时也克隆响应的引用类型成员变量。

————————————–

以下通过国宝熊猫的克隆来理解浅拷贝与深拷贝:

熊猫类包含两个成员属性:熊猫名字与饲养员。熊猫名字为普通字符串对象,饲养员为引用对象。以下分别通过浅拷贝和深拷贝两种方式克隆熊猫对象:

浅拷贝方式:

package net.mosang.prototype;
import java.util.Date;
/**
 * 
 * @author heiry
 *
 */
public class Panda implements Cloneable {
  private String name;
  private Feeder feeder;
  
  //重写clone方法
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Panda panda = (Panda) super.clone();
    return panda;
  }

  //setter、getter
  public String getName() {
    return name;
  }


  public void setName(String name) {
    this.name = name;
  }

  public Feeder getFeeder() {
    return feeder;
  }

  public void setFeeder(Feeder feeder) {
    this.feeder = feeder;
  }


  //构造方法

  public Panda(String name, Feeder feeder) {
    super();
    this.name = name;
    this.feeder = feeder;
  }
  
  }
package net.mosang.prototype;

public class Feeder {
  public String name;
  public void feed() {
    System.out.println("feed panda....");
  }
  public Feeder(String name) {
    super();
    this.name = name;
  }
  
}
package net.mosang.prototype;
public class Client {

  public static void main(String[] args) throws CloneNotSupportedException {
    Feeder feeder = new Feed犀利士
er("张大胖");
    Panda p1 = new Panda("pangpang",feeder);
    Panda p2 = (Panda) p1.clone();
    System.out.println(p1);
    System.out.println(p2);
    feeder.name = "张小胖";
    System.out.println("p1----->"+p1+"p1饲养员:----->"+p1.getFeeder().name);
    System.out.println("p2----->"+p2+"p2饲养员:----->"+p2.getFeeder().name);
  }
}

运行结果:

net.mosang.prototype.Panda@15db9742
net.mosang.prototype.Panda@6d06d69c
p1----->net.mosang.prototype.Panda@15db9742p1饲养员:----->张小胖
p2----->net.mosang.prototype.Panda@6d06d69cp2饲养员:----->张小胖

可见浅拷贝方式,虽然获得了一个与原型对象一样的新对象,但是对于引用类型成员变量feeder,拷贝的是地址而不是对象本身,换句话说,两个对象的feeder指向的是同一个Feeder对象。

深拷贝方式:

重点在于重写原型类的clone方法:

package net.mosang.prototype;
/**
 * 
 * @author heiry
 *
 */
public class Panda implements Cloneable{
  private String name;
  private Feeder feeder;
  
  //重写clone方法,并将引用成员变量feeder同时克隆
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();
    Panda panda = (Panda) obj ;
    panda.feeder = (Feeder)this.feeder.clone();
    return panda;
  }

  //setter、getter
  public String getName() {
    return name;
  }


  public void setName(String name) {
    this.name = name;
  }

  public Feeder getFeeder() {
    return feeder;
  }

  public void setFeeder(Feeder feeder) {
    this.feeder = feeder;
  }
  //构造方法
  public Panda(String name, Feeder feeder) {
    super();
    this.name = name;
    this.feeder = feeder;
  }
  }
package net.mosang.prototype;

public class Feeder implements Cloneable{
  public String name;
  public void feed() {
    System.out.println("feed panda....");
  }
  public Feeder(String name) {
    super();
    this.name = name;
  }
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
  
}

 

package net.mosang.prototype;
public class Client {

  public static void main(String[] args) throws CloneNotSupportedException {
    Feeder feeder = new Feeder("张大胖");
    Panda p1 = new Panda("pangpang",feeder);
    Panda p2 = (Panda) p1.clone();
    System.out.println(p1);
    System.out.println(p2);
    feeder.name = "张小胖";
    System.out.println("p1----->"+p1+"p1饲养员:----->"+p1.getFeeder().name);
    System.out.println("p2----->"+p2+"p2饲养员:----->"+p2.getFeeder().name);
  }
}

运行结果:

net.mosang.prototype.Panda@15db9742
net.mosang.prototype.Panda@6d06d69c
p1----->net.mosang.prototype.Panda@15db9742p1饲养员:----->张小胖
p2----->net.mosang.prototype.Panda@6d06d69cp2饲养员:----->张大胖

可以看到,深拷贝的引用类型成员变量也是各自独立的。