枚举,线程生命周期,单例设计模式,线程池

枚举

作用:限制某个类创建对象的个数。

枚举也是一个特殊的类,编译后也会生成一个class文件,他的构造方法都是private的。

想要创建某个类必须要通过他的构造方法,在jdk5.0之前,我们要想限制某个类创建对象的个数,我们可以把这个类的构造方法私有化,这样别的类就不能通过构造方法创建这个类的对象了,但是我们还想要这个类有对象给其他类使用,这个时候我们可以在这个类内创建自己的对象,再把它定义成静态的,这样就可以通过类名.对象名直接访问了,我们也不想对象的个数不会因为调用它的类给他赋值为null而导致此对象引用为空变成垃圾被回收。

class Grade{	//A B C D E 表示成绩
	private Grade(){//不想要外界创建这个类的对象,我们可以把这个类的构造方法定义成private的
		
	}
/*在本类中创建自己的对象,定义成public static的,这样别的类要向使用这个类的对象就可以通过类名.对象名直接调用,同时我们
不想要外界随意修改这个对象,可以把这个对象声明成final的*/
	public static final Grade A=new Grade();
	public static final Grade B=new Grade();
	public static final Grade C=new Grade();
	public static final Grade D=new Grade();
	public static final Grade E=new Grade();
	
	public void test(){
		System.out.println("test");
	}
	private void test2(){
		System.out.println("这个方法不能被调用");
	}
}
public class Demo8{
	public static void main(String[] args) {
		Grade a=Grade.A;//通过类名.对象名直接调用这个类的对象
		a.test();//可以直接调用该类中的公共方法
//		a.test2();//私有化不能调用
	}
}

可以写成枚举类:

enum Grade2{    //结果:A,10
	A,B,C,D,E;   //;可省略
	int x=10;//成员变量要定义在枚举的下面
	public void test(){
		System.out.println("test");
	}
}

public class Demo2 {
	public static void main(String[] args) {
//相当于前面说的静态final,可以直接类名.对象名调用,由于枚举类中已经重写了toString方法,所以会直接显示对象名
		System.out.println(Grade2.A);	
		System.out.println(Grade2.A.x);
	}
}

枚举的应用

enum Gender{ 	//定义了性别类,这个类只有两个对象,限定了界限
	MAN,WOMEN
}

class Person{
	private String name;
	private int age;
	Gender sex;	//sex的类型为枚举类型,同包下只要定义了枚举就可以直接使用,枚举限定了值的选择,解决了随便赋值的问题
	public Person(String name, int age, Gender sex) {
		super();
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	@Override
	public String toString() {
		return name+"..."+age+"..."+sex;
	}
}
public class Demo3 {	//zhangsan...20...MAN
	public static void main(String[] args) {
		Person p=new Person("zhangsan", 20, Gender.MAN);
		System.out.println(p);
	}
}

枚举可以用在switch语句中,switch支持byte,int,short,char,string,enum 类型的变量

//枚举可以用在switch语句中
enum Grade3{
	A,B,C,D;
}

public class Demo4 {
	public static void main(String[] args) {
		switch (Grade3.A) {
		case A:
			System.out.println("A");
			break;
		case B:
			System.out.println("B");
			break;
		case C:
			System.out.println("C");
			break;
		case D:
			System.out.println("D");
			break;

		default:
			break;
		}
	}
}

枚举类中的成员

变量,静态变量,方法,静态方法,构造方法(private),抽象方法(必须要实现)。构造方法必须是私有的,也可以有重载形式,调用重载的构造方法,枚举类的变量也要加上实参来和重载的构造方法匹配。

枚举类的构造方法也有重载形式,重载的构造方法也必须是private的,只要在枚举后面加个括号,里面写上构造方法对应的实参即可。默认构造参数如果有输出语句的话,即使main方法不调用别的对象,这个构造函数的输出语句也会默认打印,因为所有的对象都已经创建出来了。

enum Week {
	MON(5) {
		@Override
		void info() {
			System.out.println("星期一");
		}
	},
	TUE {
		@Override
		void info() {
			System.out.println("星期二");
		}
 
	};
	private Week() {//默认无参构造方法,enum的构造方法必须是private的
		System.out.println("测试,每有一个对象都会调用此构造方法,即使在main方法中没有调用别的对象也会输出这句话");
	}
	private Week(int num){//有参构造方法
		System.out.println(num);
	}
 
	// The enum constants must implement the abstract method
	// info(),有抽象方法必须要实现,可以使用匿名内部类的方式
	abstract void info();
}
 
public class Demo3 {
	public static void main(String[] args) {
		
/*只要调用了一个枚举对象,构造方法就会输出打印所有的枚举对象,如果他的构造方法中有输出语句即使你没有输出这个对象,
 构造方法中的输出语句也会默认输出到控制台,所以此处会先输出有参构造函数中的5,然后再输出无参构造函数中的那句话,且只会输出一次*/
		Week w = Week.MON;
		Week w2=Week.TUE;
		Week[] values = Week.values();
		for(Week w3:values) {
			System.out.println(w3+"___");
		}
		System.out.println("==="+w.name());
		System.out.println(w2.compareTo(w));
		w.info();// 调用他本身已经重写的info()方法,
		w2.info();
		System.out.println("w.ordinal()--"+w.ordinal());// 枚举对象的序号,默认从0开始
		System.out.println("w2.ordinal()--"+w2.ordinal());
	}
}

 

常用方法

枚举.values(): 获取所有枚举值名,返回的是枚举值名的数组,遍历后即可得到所有的枚举值

name(): 获取指定的枚举名相当于toString方法

compareTo(): 比较两个枚举的顺序,返回一个int值,是这两个枚举值之间序号的差值,同一个枚举值返回的是0

ordinal():返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)

线程生命周期

生命周期指的是线程从无到创建再到无的过程。

线程可以总结有以下状态:

新建状态new Thread()或者是new Thread(Runnable)

就绪状态start()方法,有运行资格,等待CPU调度

运行状态:执行run()

消亡状态run()执行完了

阻塞状态:运行过程中,sleep(millis),wait()-notify(),I/O,这时候主动放弃执行资格,CPU不再调度,阻塞解除后就会回到就绪状态等待再次调度,而不会直接回到运行状态。

注意:上述状态只是理论上的,java中有一个类专门描述线程状态的---Thread.State

参考文章:https://blog.csdn.net/pange1991/article/details/53860651

IO时的线程状态以及CPU状态:https://my.oschina.net/goldenshaw/blog/705397

 

NEW (新建状态)
          至今尚未启动的线程的状态。
RUNNABLE (就绪状态+运行状态)//启动start()之后就会出现这种状态
          可运行线程的线程状态。
BLOCKED (阻塞状态)//带同步锁的时候会出现这种状态
          受阻塞并且正在等待监视器锁的某一线程的线程状态。

TIMED_WAITING (阻塞状态)//主线程睡一段时间,自己也睡一段时间才会出现sleep()方法
          具有指定等待时间的某一等待线程的线程状态。

WAITING (阻塞状态)//执行了wait()方法会出现
          某一等待线程的线程状态。
TERMINATED (消亡状态)
          已终止线程的线程状态。

下面要运用Thread类中的getState()方法验证以上状态

 Thread.StategetState() 
          返回该线程的状态。

 

创建线程的四种方式

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

4)使用线程池例如用Executor框架

具体参考如下链接:https://blog.csdn.net/m0_37840000/article/details/79756932

class myThread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class Demo6 {
	public static void main(String[] args) throws InterruptedException {
		Thread t=new Thread(new myThread());
		System.out.println(t.getState());//线程还没开始,现在应该是新建状态,返回new
		t.start();
		System.out.println(t.getState());//开启线程,现在应该是就绪状态+运行状态,返回RUNNABLE
		Thread.sleep(1000);
		System.out.println(t.getState());
	}
}

单例设计模式

设计模式:通用问题的通用解决方案,共23种,将这些模式分为:创建型,行为型,结构型

单例模式:

作用:保证内存中对象的唯一性。

步骤:

  1. 私有化构造方法
  2. 在本类中创建该类的实例对象
  3. 在类中创建一个公共的方法,用于返回该类实例对象。

饿汉式+懒汉式:这单例设计模式效果和枚举以及私有化构造方法然后定义final本类对象效果一样。

懒汉式:类一加载对象就创建好了,如果不需要对象,可能会造成空间浪费;

饿汉式:什么时候使用才创建对象---对象的延时加载。弊端:多线程访问时可能出现线程不安全,对象不唯一。解决办法是加一个同步锁,锁对象用类名.class即可,因为方法是静态的。

//单例模式-饿汉式
class Single1 {// 类一加载对象就创造好了,可能会造成空间浪费,线程安全

	private Single1() {}// 1.私有化构造方法

	private static final Single1 s = new Single1();// 2.在本类中创建该类实例对象

	public static Single1 getInstance() {// 3.在类中创建一个方法,用于返回该类实例对象
		return s;
	}
}

// 单例模式-懒汉式
class Single2 {// 什么时候用什么时候创建对象,不会造成空间浪费---对象的延时加载
	// 由于该类对象不是一步创建完成的,多线程访问时可能会出现对象不唯一,不安全,解决办法:加一个同步锁
	private Single2() {}// 1.私有化构造方法

	private static Single2 s = null;// 2.在本类中创建自己的对象,注意这里千万不能用final修饰,否则下面就不能对s赋值了

	public static Single2 getInstance() {// 3.创建一个公共方法,用于返回该类实例对象
		if (s == null)//不等于null时直接拿来使用就可以,不用再判断同步锁
			synchronized (Single2.class) {// 加一个同步锁,防止线程不安全

/*不加的话第一次s获得了一个对象new Single2,第二次进来调用方法,此时s已经有一个new Single2的对象了,但是没有判断是否为空,
于是他又创建了一个new Single2的对象,每new一次都是一个新的对象,地址值不同,所以两次不相等*/
				if(s==null)
				s = new Single2();
			}
		return s;
	}
}

public class Demo7 {
	public static void main(String[] args) {
		Single1 s1 = Single1.getInstance();
		Single1 s2 = Single1.getInstance();
		System.out.println(s1 == s2);// true,证明对象唯一

		Single2 s3 = Single2.getInstance();
		Single2 s4 = Single2.getInstance();
		System.out.println(s3 == s4);// true,证明对象唯一
	}
}

懒汉式三种线程安全的写法(双重检测可以用volatile关键字修饰省略一层检测)

1、在getInstance方法上加同步

public static synchronized Singleton getInstance() {  
    if (single == null) {    
        single = new Singleton();  
    }    
    return single;  
} 

2.1、双重检查锁定

public static Singleton getInstance() {  
    if (singleton == null) {    
        synchronized (Singleton.class) {    
            if (singleton == null) {    
                singleton = new Singleton();   
            }    
        }    
    }    
    return singleton;   
} 

2.2、volatile关键字

public class Singleton {
    //这样的写法是能避免无序写入的问题。因为别的线程进入不了方法体,除非当前线程释放锁。这样就能确保实例化完成。
    private volatile static Singleton instance = null;   
    private Singleton() {}   
    public static Singleton getInstance() {   
        synchronized (Singleton.class) { 
            if (instance == null) {          
                instance = new Singleton();     
            }   
        }   
  
        return instance;   
    }   
}  

3、静态内部类

public class Singleton {
    // 静态内部类初始化即加载,且仅加载一次,保证了对象的唯一性
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton();    
    }    
    private Singleton (){}    
    public static final Singleton getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
} 

总结:

静态内部类这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

优点:

(1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。 
(2)由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,比如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。 
(3)单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。 
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。

 

缺点:

(1)单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。 
(2)单例对象如果持有Context,那么很容易引发内存泄露,此时需要注意传给单例对象的Context最好是Application Context。

线程池

由于创建和销毁线程耗费时间比任务时间长,因此,为了解决这个问题,出现了线程池的思想。即将创建好的线程对象存放到线程池中,这些线程对象,等待接受任务,接收到任务就去执行,任务结束就回到线程池。如果线程池中没有可用的线程,则该任务就会等待,直到有空闲线程来接收任务。

创建线程池对象:java.util.concurrent.Excutors

newFixedThreadPool(int nThreads):创建固定个数的线程对象的线程池。返回ExcutorService对象

newSingleThreadExcutorService(): 创建单个线程对象的线程池。返回ExcutorService对象

static ExecutorServicenewFixedThreadPool(int nThreads, ThreadFactory threadFactory) 
          创建一个可重复使用的固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。
  1.  Customer customer = (Customer) super.clone();  
  2.     customer.address = address.clone();  
  3.     return customer; 

ExcutorService接口中的方法:

 Future<?>submit(Runnable task) 
          提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
 voidshutdown() 
          启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
 List<Runnable>shutdownNow() 
          试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyRun2 implements Runnable{
	public void run(){
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"...."+i);
		}
	}
}

class MyRun3 implements Runnable{
	public void run(){
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"--------"+i);
		}
	}
}

class MyRun4 implements Runnable{
	public void run(){
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"==============="+i);
		}
	}
}


public class Demo8 {
	public static void main(String[] args) {
		//创建线程池对象,有两个线程对象
		ExecutorService es = Executors.newFixedThreadPool(2);
		//将任务进行提交,2,3先交替运行,等到有一个线程结束后再接手下一个任务
		es.submit(new MyRun2());
		es.submit(new MyRun3());
		es.submit(new MyRun4());
		
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值