Java 多线程(2)——Java学习笔记25(补)

一 设计模式之 单例设计模式

1.解释

保证类在内存中只有一个对象

2.三种创建方式

(1)饿汉式

//饿汉式,一上来就创建本类对象
class Singleton{
	//1.私有构造方法,其他的类不能访问构造器了
	private Singleton() {}
	//2.创建本类对象,私有成员变量
	private static Singleton s = new Singleton();
	//3.提供公共的get访问方法
	public static Singleton getInstance() {
		return s;
	}
}

(2)懒汉式(单例的延迟加载模式)

class Singleton1{
	//1.私有构造方法,其他类无法访问构造器
	private Singleton1() {}
	//2.声明一个本类成员变量,不创建实例,私有成员变量
	private static Singleton1 s1;
	//3.提供公共访问方法,如果在内存中有本类的实例,直接返回,没有就创建一个
	public static Singleton1 getInstance() {
		if (s1 == null) {
			s1 = new Singleton1();
		}
		return s1;
	}
}
弊端

多线程的时候可能会创建多个实例对象

(3)第三种方式

class Singleton2{
	
	private Singleton2() {}
	
	private static final Singleton2 s = new Singleton2();
	
}

3.案例:使用了单例设计模式的类:Runtime

Runtime类使用了饿汉式单例设计模式

	private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}

二 计时器:Timer类

1.Constructor

	Timer();

2.Method

	schedule(TimeTask task,Date time);				//传入任务子类,触发的时间
	schedule(TimerTask task, Date firstTime, long period);		//传入任务子类,首次触发时间,触发间隔毫秒

3. TimeTask类

(1)功能

创建一个TimeTask的子类,然后重写run()方法

(2)e.g

class MyTimeTask extends TimerTask{
	@Override
	public void run() {
		System.out.println("起床背单词!!!");
	}
}

4. e.g

public static void main(String[] args) throws InterruptedException {
	Timer t = new Timer();
	t.schedule(new MyTimeTask(), new Date(119, 2, 14, 20, 9, 0));
	
	while (true) {			//每隔1秒钟打印一次时间
		Thread.sleep(1000);
		System.out.println(new Date());
	}
}

三 线程间的通信

1.什么时候需要通信

需要让多个线程间进行有规律的执行的时候

等待唤醒 机制

1.Object类中的方法

	//Object类的方法
	void notify();		//唤醒在此对象监视器上等待的单个线程
	void notifyAll();	//唤醒在此对象监视器上等待的所有线程
	void wait();		//在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。
						//throws InterruptedException

2. e.g 两个线程的 唤醒 等待

public class Demo1_Notify {
	public static void main(String[] args) {
		Printer p = new Printer();
		
		new Thread() {
			public void run() {
				while (true) {
					try {
						p.print1();
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread() {
			public void run() {
				while (true) {
					try {
						p.print2();
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
				}
			}
		}.start();
	}

}

class Printer {
	int flag = 1;
	
	public void print1() throws InterruptedException {
		synchronized (this) {
			if (flag != 1) {
				this.wait();
			}
			System.out.print("钱");
			System.out.print("惊");
			System.out.print("涛");
			System.out.print("\r\n");
			flag = 2;
			this.notify();
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized (this) {
			if (flag != 2) {
				this.wait();
			}
			System.out.print("黄");
			System.out.print("麒");
			System.out.print("宁");
			System.out.print("\r\n");
			flag = 1;
			this.notify();
		}
	}
}

3. e.g 三个及以上线程的 唤醒 等待

(1)分析
  1. wait()方法是从哪里停止,再从哪里开始,所以当线程被唤醒的时候,执行的语句是从wait()的后面开始的,并不会重新执行本线程的语句;
  2. 若要让等待的线程被唤醒时重新判断flag的值,将if()语句改为while(),只有当flag==当前线程要求的flag时才会跳出循环;
  3. 但是当notify()随机唤醒的线程均不是flag能通过的线程时(特殊情况),所有线程都处于等待状态,程序无法继续运行了;
  4. 所以将notify()方法改为notifyAll()方法,唤醒所有的线程,进行flag的比较。
(2)代码
public class Demo2_NotifyAll {

	public static void main(String[] args) {
		Printer2 p = new Printer2();
		//这里省略启动三个线程分别执行print1、2、3方法的过程
}

class Printer2 {
	int flag = 1;
	
	public void print1() throws InterruptedException {
		synchronized (this) {
			while(flag != 1) {
				this.wait();		//当前线程等待
			}
			System.out.print("钱");
			System.out.print("惊");
			System.out.print("涛");
			System.out.print("\r\n");
			flag = 2;
			this.notify();			//随机唤醒单个等待的线程
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized (this) {
			while (flag != 2) {
				this.wait();
			}
			System.out.print("黄");
			System.out.print("麒");
			System.out.print("宁");
			System.out.print("\r\n");
			flag = 3;
			this.notify();
		}
	}
	
	public void print3() throws InterruptedException {
		synchronized (this) {
			while (flag != 3) {
				this.wait();
			}
			System.out.print("爱");
			System.out.print("的");
			System.out.print("魔");
			System.out.print("丽");
			System.out.print("转");
			System.out.print("圈");
			System.out.print("圈");
			System.out.print("\r\n");
			flag = 1;
			this.notify();
		}
	}
}

4. 线程间通信的注意事项

  1. 同步代码块中的同步锁对象用来调用wait()和notify()方法。(用this当作同步锁,就用this来调用线程等待和唤醒的方法);
  2. wait()和notify()方法定义在Object中,因为锁对象可以是任意对象;
  3. sleep()&wait()方法的区别:
  • sleep()方法必须传入参数,参数就是时间,时间到了,自动醒来;
  • wait()方法可以传入参数,也可以不传入参数,传入参数就是再参数的时间结束后等待,不传入就是直接等待。
  • sleep()方法在同步函数函数或同步代码块中不释放锁;
  • wait()方法在同步函数或同步代码块中释放锁。

5. JDK1.5新特性:互斥锁(ReentrantLock)

(1)同步

使用ReentrantLock类中的lock()&unlock()方法进行同步

(2)Method
	void lock();
	void unlock();
	Condition newCondition();
(3)Condition接口
Method
	void await();
	void signal();
	void signalAll();
(4)改写之前的方法
class Printer3 {
	private ReentrantLock r = new ReentrantLock();
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	
	
	int flag = 1;
	
	public void print1() throws InterruptedException {
		r.lock();
			if(flag != 1) {
				c1.await();						//当前线程等待
			}
			System.out.print("钱");
			System.out.print("惊");
			System.out.print("涛");
			System.out.print("\r\n");
			flag = 2;
			c2.signal();					//随机唤醒单个等待的线程
		r.unlock();
	}
	
	
	public void print2() throws InterruptedException {
		r.lock();
			if (flag != 2) {
				c2.await();
			}
			System.out.print("黄");
			System.out.print("麒");
			System.out.print("宁");
			System.out.print("\r\n");
			flag = 3;
			c3.signal();
		r.unlock();
	}
	
	public void print3() throws InterruptedException {
		r.lock();
			if (flag != 3) {
				c3.await();
			}
			System.out.print("爱");
			System.out.print("的");
			System.out.print("魔");
			System.out.print("力");
			System.out.print("转");
			System.out.print("圈");
			System.out.print("圈");
			System.out.print("\r\n");
			flag = 1;
			c1.signal();
		r.unlock();
	}
}

四 线程组(ThreadGroup类)

(1)概述

  • ThreadGroup表示线程组,可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制
  • 默认情况下,所有的线程都属于主线程组(main)

(2)Constructor&Method

Thread的构造中,有添加线程组的构造器

	//target为Thread的运行对象,name为Thread的名称,group是该线程所在的线程组
	public Thread(ThreadGroup group, Runnable target,String name);		
	public Thread(ThreadGroup group, Runnable target);	
	public Thread(ThreadpGroup group, String name);

Thread的方法中,获取线程组的方法

public ThreadGroup getThreadGroup();

ThreadGroup中的方法

	void setDaemon(boolean daemon);		//设置线程组是否为守护线程
	String getName();

五 线程的5中状态

  • 新建,Thread t1 = new Thread(xxx);
  • 就绪,t1.start();notify();
  • 运行,得到cpu执行权,调用t1.run();
  • 阻塞, sleep(),wait()等,唤醒后重新进入就绪状态
  • 死亡,run()结束

六 线程池(Executors类)

(1)概述

线程的启动成本很高,涉及到操作系统的交互。当程序中需要创建大量的生存周期很小的线程时,应使用线程池。线程池中的线程在结束后不会立刻死亡,而是返回线程池中处于空闲状态,等待再次被调用。

(2)Method

	public static ExecutorService newFixedThreadPool(int nThreads);		//创建线程池服务器,池中放nThread条线程
	public static ExcutorService newSingleThreadExecutor();				//创建一个只放一条线程的线程池

(3)ExecutorService

Method

public Future<T> submit(Runnable task);				//提交一个Runnable任务用于执行,返回一个该任务的Future。
public viud shutDown();								//结束线程池

七 多线程实现的第三种方式(Callable)

除了extends Thread和implements Runnable之外。

e.g

public class Demo06_Callable {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService pool = Executors.newFixedThreadPool(2);
		Future<Integer> f1 = pool.submit(new MyCallable(100));
		Future<Integer> f2 = pool.submit(new MyCallable(1000));
		
		System.out.println(f1.get());
		System.out.println(f2.get());
		pool.shutdown();
	}
}
class MyCallable implements Callable<Integer>{
	private int num;
	public MyCallable(int num) {
		this.num = num;
	}
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for(int i = 1; i <= num ; i++) {
			sum += i;
		}
		return sum;
	}
}

八 设计模式 工厂模式

(1)(静态工厂方法模式) 概述

定义一个具体的工厂类来负责创建一些类的实例,需要创建实例时直接调用工厂类的静态方法创建即可。

  • 优点:客户端不需要再负责对象的创建,从而明确了各个类的职责;
  • 弊端:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护。

(2)(工厂方法模式)概述

① 方法

  1. 创建工厂接口 interface Factory,定义抽象方法
  2. 创建猫类狗类
  3. 创建猫狗工厂类实现工厂接口,重写抽象方法
  4. 在测试类中先实现猫狗工厂对象,通过工厂对象创建猫狗实例对象
② 优缺点
  • 优点
    • 客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
  • 缺点
    • 需要额外的编写代码,增加了工作量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值