多线程

 多线程概述

      线程是程序执行的一条路径, 一个进程中可以包含多条线程
  多线程并发执行可以提高程序的效率, 可以同时完成多项工作

并发和并行的区别

并行:就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行,需要多核CPU。

并发:是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行

多线程的实现

       方式一:

继承Thread

public static void main(String[] args) {
		//第四步创建子类对象
		Mythead my = new Mythead();
		//第五步开启线程
		my.start();
		
		for (int i = 0; i < 1000; i++) {
			System.out.println(my.getName() + "。。。第二条线程");
		}
	}
}	
//第一步 继承Thread

class Mythead extends Thread {
	//第二步 重写run方法
	public void run(){
		//第三步 把要做的事写入run方法中
		for (int i = 0; i < 1000; i++) {
			System.out.println("新实现的线程");
		}
	}
}

      方式二:

实现Runnable接口

public static void main(String[] args) {
		//第四步:创建定义类的对象
		MyRunnable myr = new MyRunnable();
		//第五步:创建Thread对象,将定义类的对象当作参数传入Thread的构造中,线程开启后后自动调用Runnable的run方法
		Thread t = new Thread(myr);
		//第六步:开启线程
		t.start();
		
		for (int i = 0; i < 1000; i++) {
			System.out.println("hhhhhhhhhhhh");
		}
		
	}
}
//第一步:定义类实现Runnable接口
class MyRunnable implements Runnable{

	@Override//第二步:重写run方法
	public void run() {
		//第三步:将要做的方法放在run方法中
		for (int i = 0; i < 1000; i++) {
			System.out.println("hhhhhh");
		}
		
	}

两种方式的区别:

              查看源码的区别:
a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
        b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空,                     编译时看的是Runnable的run(),运行时执行的是子类的run()方法

    继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
        弊端是:如果已经有了父类,就不能用这种方法
    实现Runnable接口
 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

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

这样相对简单些,节省了很多代码

public static void main(String[] args) {
		
		//继承Thread
		new Thread(){//第一步:new 类(){}继承类
			public void run(){//第二步:重写run方法
				for (int i = 0; i < 10000; i++) {//第三步:将要执行的代码放在方法中
				
					System.out.println( "第一种");
				}
			}
		}.start();//第四步:调用start方法开启线程
		
		//实现Runnable接口
		new Thread(new Runnable(){//第一步:new 接口(){}实现接口
			@Override
			public void run() {//第二步:重写run方法
				for (int i = 0; i < 10000; i++) {//第三步:将要实现的代码放在方法中
					System.out.println("这是第二种");
					
				}
			}
		}).start();//第四步:开启线程
	}
}

获取和设置线程的名字

使用SetName方法设置名字:

public static void main(String[] args) {
		Thread t1 = new Thread(){
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(getName() + "....运行线程");
				}
			}
		};
		
		
		Thread t2 = new Thread(){
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(getName() + "。。。。.......运行线程");//使用getName获取名字
				}
			}
		};
		
		t1.setName("第一条");//使用set方法设置名字
		
		t2.setName("第二条");
		
		t1.start();
		t2.start();
	}

}

传入参数设置名字

public static void main(String[] args) {
		new Thread("参数设置名字1"){                        //向Thread构造内传入String类型的名字
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(getName()+ "...线程运行");//通过getName方法获取名字
				}
			}
		}.start();
		new Thread("参数设置名字2"){                           //向Thread构造内传入String类型的名字
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(getName()+ "...线程运行");//通过getName方法获取名字
				}
			}
		}.start();
	}
实现接口时设置名字

public static void main(String[] args) {
		
	Thread t1 =	new Thread(new Runnable(){//<span style="font-family: Arial, Helvetica, sans-serif;">方法一:</span>

			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(Thread.currentThread().getName()+".............");
				}                       //获取当前对象的引用调用getName方法     获取名字的方法
			}
	   },"第一");//.start();方法二:
		//Thread(Rannable ,String name)这样也可以设置名字
		
		Thread t2 =new Thread(new Runnable(){
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println(Thread.currentThread().getName()+".............好好");
				}                      //获取当前对象的引用调用getName方法
			}
		},"第二");//.start();
		//Thread(Rannable ,String name)这样也可以设置名字
		//t1.setName("第一条线程");//设置和修改名字
		//t2.setName("第二条线程");//设置和修改名字
		
		t.start();
		t2.start();
	}

}

休眠线程

//Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000
	public static void main(String[] args) {
		 new Thread(new Runnable(){
			@Override
			public void run() {
				
				for (int i = 0; i < 10; i++) {
					try {                  //子类继承父类方法,父类中没有抛出异常子类遇有异常不能抛,只能处理
					Thread.sleep(10);//休眠10毫秒
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
					System.out.println(Thread.currentThread().getName() + "输出一个");
				}
			}
		}).start();
		 
		 
		 new Thread(new Runnable(){
			 @Override
			 public void run() {
				 try {
					 
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				 for (int i = 0; i < 10; i++) {
					 System.out.println(Thread.currentThread().getName() + "。。。。输出另一个");
				 }
			 }
		 }).start();
		
		
	}

守护线程

public static void main(String[] args) {
		Thread t = new Thread(){
			public void run() {
				for (int i = 0; i < 50; i++) {
					//Thread.sleep(10);
					System.out.println(getName() + "第一次输入");
				}
			}
		};
		
		
		Thread t1 = new Thread(){
			public void run() {
				for (int i = 0; i < 2; i++) {
					//Thread.sleep(10);
					System.out.println(getName() + "。。。。。。。。。。第一次输入");
				}
			}
		};
		t.setDaemon(true);//设置守护线程
		//setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
		//老大死了,小弟也就消失
		t.start();
		t1.start();
	}
加入线程

public static void main(String[] args) {
		final Thread t = new Thread (){
			public void run() {
				for (int i = 0; i < 100; i++) {
					
					System.out.println(getName() + "生日快乐");
				}
			}
		};
		
		
		Thread t2 = new Thread (){
			public void run() {
				for (int i = 0; i < 100; i++) {
					if(i==3){
						try {
							t.join();//插入线程当前线程暂停,当插入线程运行完后再继续
							               //插入线程,当前线程停30毫秒
							//t.join(30);//匿名内部类在访问这个方法 的局部变量时,变量要用final修饰
						} catch (InterruptedException e) {
							
							e.printStackTrace();
						}
					}
					
					System.out.println(getName() + "。。。祝你自己生日快乐");
				}
			}
		};
		
		
		t.start();
		t2.start();
	}

同步代码块

同步代码块的使用时机:

 当多线程并发, 有多段代码同时执行时, 如果希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.


 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.

怎样实现代码块的同步:

public static void main(String[] args) {
		final Printer1 p = new Printer1();
		new Thread(){//匿名内部类在使用方法中的局部变量时必须用final修饰
			public void run() {
				while(true){
					p.print1();
					p.print2();
				}
			}
		}.start();
	}
	
}
/*使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
* 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的*/
class Printer1{
		T fd = new T();
	public  void print1(){
		synchronized(fd){//获取锁,,任意对象可以当锁传递
			//synchronized(new T()){//匿名对象不可以可以当锁传递,,因为不能确定匿名对象是同一对象也就不能确定是否同步
			
		System.out.print("大");
		System.out.print("家");
		System.out.print("都");
		System.out.print("祝");
		System.out.print("我");
		System.out.print("\r\n");
		}              //释放所
	}
	
	
	public  void print2(){
		synchronized(fd){
			//synchronized(new T()){
		System.out.print("生");
		System.out.print("日");
		System.out.print("快");
		System.out.print("乐");
		System.out.print("哈");
		System.out.print("\r\n");
		}
	}
	}
	class T{}
synchronized修饰的方法:

public static void main(String[] args) {
		final Printer3 p = new Printer3();
		new Thread(){
			public void run(){
				p.print1();
				p.print1();
			}
		}.start();
	}

}

class Printer3{
	//synchronized 修饰的方法内全部代码都是同步的
	//如果想方法中的部分代码那就用synchronized修饰代码块
	//如果想用所有的代码就修饰方法
	
public synchronized void print1(){
	
		
	System.out.print("大");
	System.out.print("家");
	System.out.print("都");
	System.out.print("祝");
	System.out.print("我");
	System.out.print("\r\n");
	
}


public synchronized void print2(){
	
	System.out.print("生");
	System.out.print("日");
	System.out.print("快");
	System.out.print("乐");
	System.out.print("哈");
	System.out.print("\r\n");
	
}
}
静态方法的同步
public static void main(String[] args) {
		final Printer p = new Printer();
		new Thread(){
			public void run(){
				p.print1();
				p.print1();
			}
		}.start();
	}

}
class Printer{
	//T fd = new T();
public static void print1(){
	synchronized(Printer.class){
		
	System.out.print("大");
	System.out.print("家");
	System.out.print("都");
	System.out.print("祝");
	System.out.print("我");
	System.out.print("\r\n");
	}              //释放锁
}


public static void print2(){
	synchronized(Printer.class){//静态的方法中代码块要同步插入 字节码对象当锁       静态的同步函数的锁是:字节码对象
		                                                       //非静态同步函数的锁是:this
	System.out.print("生");
	System.out.print("日");
	System.out.print("快");
	System.out.print("乐");
	System.out.print("哈");
	System.out.print("\r\n");
	}
}
}

死锁
//多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
	//开发时尽量避免出现同步代码块的嵌套
	private static String s1 = "筷子一";
	private static String s2 = "筷子一";
	public static void main(String[] args) {
		new Thread(){
			public void run(){
				while(true){
					synchronized(s1){
						System.out.println(getName() +"获取" + s1 + "等待" + s2 );
						synchronized(s2){
							System.out.println(getName() +"获取" + s2 + "等待用餐" );
						}
					}
					
				}
			}
		}.start();
	
	
	new Thread(){
		public void run(){
			while(true){
				synchronized(s2){
					System.out.println(getName() +"获取" + s2 + "等待" + s1 );
					synchronized(s1){
						System.out.println(getName() +"获取" + s1 + "等待用餐" );
					}
				}
				
			}
		}
	}.start();
}

单例设计模式

        作用是保证类在内存中只有一个对象。

步骤:

public static void main(String[] args) {
		/*Singleton s1 = Singleton.getS();
		Singleton s2 = Singleton.getS();
		
		System.out.println(s1==s2);*/
		
		Singleton s1 = Singleton.s;
		Singleton s2 = Singleton.s;
		
		System.out.println(s1==s2);
	}
	
}//
/*class Singleton{
	private Singleton(){}//私有构造方法,不让其他类创建本类对象
	
	private static Singleton s = new Singleton();//创建本类对象
	
	public static Singleton getS(){//对外提供公共访问方式,
		return s;//返回本类对象
	}
}*/
//懒汉式  单例的延迟加载
/*class Singleton{
	private Singleton(){}//私有构造方法 ,不让其他类创建本类对象
	
	private static Singleton s ;//声明本类对象
	
	public static Singleton getS(){//对外提供公共访问方式
		if(s==null){
			s=new Singleton();//判断
		}
		return s;
	}
}*/
/*饿汉式和懒汉式的区别
 * 都是单线程时
 * 饿汉是空间换时间
 * 懒汉式是时间换空间
 * 都是多线程时
 * 饿汉式没有安全隐患
 * 懒汉式是可能出现安全隐患的,因为有可能创建多个对象*/
class Singleton{//第三种方法
	private Singleton(){}//私有构造方法
	public final static Singleton s = new Singleton();
}//创建本类公共的静态的final修饰的对象
Runtime类

是单例类

	public static void main(String[] args) throws IOException {
		Runtime r = Runtime.getRuntime();//
		
		//r.exec("shutdown -s -t 300");//设置关机时间
		r.exec("Shutdown -a");         //取消关机

Timer类

是一个计时器类在指定的时间执行指定的任务

public static void main(String[] args) throws InterruptedException {
		Timer t = new Timer();//创建计时器对象
		t.schedule(new Myclass(),new Date(115, 3, 9, 11, 57, 30),3000);//安排指定时间,重复执行任务
		//t.schedule(new Myclass(),new Date(115, 3, 9, 11, 57, 30),);//安排指定时间,执行指定任务
		while(true){
			Thread.sleep(1000);
			System.out.println(new Date());
		}
	}

}
class Myclass extends TimerTask{
	public void run(){
		System.out.println("叫我起床");
	}
}
线程间的通信

使用时机:

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

public static void main(String[] args) {
		final Demo2 d = new Demo2();
		new Thread(){
			public void run(){
				while(true){
					try {
						d.print();
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
					
				}
				
			}
		}.start();
		new Thread(){
			public void run(){
				while(true){
					try {
						d.print2();
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
					
				}
				
			}
		}.start();

	}

}
/*等待唤醒机制
 * 在同步代码块中所对象是谁,就用哪个对象来调用wait
 * 
 * 面试题一
 * 为什么wait方法和notify方法需要定义在Object
 * 因为所有的对象都是Object的子类,而所有的对象都可以当做所对象
 * 
 * 面试题二
 * 1,sleep方法必须传入参数,参数就是时间,当时间到了,会自动醒来
 *     wait方法不是必须传入参数,如果没有参数,遇到wait就等待,如果传入参数等传入的时间到了等待
 *     2,sleep方法在同步中不释放锁
 *        wait方法在同步中释放锁*/
class Demo {
	private int flag = 1;//定义变量
	private Object obj = new Object();
	public void print() 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("呀");
			System.out.print("呀");
			System.out.print("\r\n");
			flag = 1;
			this.notify();
			//如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
		}
		
	}

1.5新特性 互斥锁


 同步
 使用ReentrantLock类的lock()和unlock()方法进行同步
 通信
 使用ReentrantLock类的newCondition()方法可以获取Condition对象
 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

public static void main(String[] args) {
		final Demo2 d = new Demo2();
	new Thread(){
		public void run(){
			while(true){
				try {
					d.print();
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				
			}
			
		}
	}.start();
	new Thread(){
		public void run(){
			while(true){
				try {
					d.print2();
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				
			}
			
		}
	}.start();
	new Thread(){
		public void run(){
			while(true){
				try {
					d.print3();
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				
			}
			
		}
	}.start();

	}
	
}



class  Demo2{
	private ReentrantLock r = new ReentrantLock();
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
private int flag = 1;
private Object obj = new Object();
public void print() throws InterruptedException{
	r.lock();//同步 ,上锁
		while(flag != 1){
			c1.await();//当前线程等待,需要等待的时候使用Condition的await()方法
		}
		System.out.print("你");
		System.out.print("还");
		System.out.print("要");
		System.out.print("\r\n");
		flag = 2;
		c2.signal();//唤醒等待的单个线程,唤醒的时候用signal()方法
		r.unlock();//释放锁
	
}
public void print2() throws InterruptedException{
	r.lock();
		while(flag != 2){
			c2.await();
		}
		System.out.print("努");
		System.out.print("力");
		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();
	while(flag != 2){
		c3.await();
	}
	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();
	
}

线程池

概述

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

内置线程池的使用步骤

public static void main(String[] args) {
		
		
		ExecutorService pool = Executors.newFixedThreadPool(3);//创建线程池对象
		MyRunnable m1 = new MyRunnable();//创建Runnable实例
		MyRunnable m2 = new MyRunnable();
		MyRunnable m3 = new MyRunnable();
		
		pool.submit(m1);//提交Runnable实例
		pool.submit(m2);
		pool.submit(m3);
		
		//pool.shutdown();//关闭线程池
	}

public class MyRunnable implements Runnable{

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "我是线程池");
		}
			
		
		
	}
	
}

简单工厂设计模式

简单工厂模式 又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
优点
     客户端不需要在负责对象的创建,从而明确了各个类的职责
缺点
    这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护

工厂方法模式

工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。

 优点
     客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性

 缺点
     需要额外的编写代码,增加了工作量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值