【JavaSE】多线程丶线程同步丶线程池

一.多线程的实现方式

1.继承Thread类
	//1.继承Thread类
	class MyThread extends Thread{
		//2.重写run方法
		public void run() {
			System.out.println("继承Thread类开启多线程");
		}
	}
	
	//3.创建自定义的线程类对象
	MyThread mt=new MyThread();
	//4.调用start()方法,jvm会开启线程执行run()方法
	mt.start();

	//简化
	new Thread(){			
		public void run() {
			System.out.println("使用匿名类简化---继承Thread类");
		}
	}.start();
2.实现Runnable接口
	//1.实现Runnable接口
	class MyRunable implements Runnable{
		//2.重写run()方法
		public void run() {
			System.out.println("实现Runnable接口开启多线程");				
		}			
	}
	
	//3.创建Thread对象,并把Runnable实现类传递进构造参数
	MyRunable mr=new MyRunable();
	Thread t=new Thread(mr);
	
	//4.调用start()方法,jvm会开启线程执行run()方法
	t.start();

	//简化
	new Thread(new Runnable() {
		public void run() {
			System.out.println("使用匿名类简化---实现Runnable接口");				
		}
	}).start();
3.两种方式区别
  • 继承Thread类可以使用Thread类的方法,代码简便,但java只支持单继承,所以如果类已经有父类就不能再继承Thread类

  • 实现Runnable接口可以解决父类问题,但代码稍复杂,且不能直接调用Thread类方法

二.线程信息

1.线程名称
	//通过构造方法设置线程名称
	new Thread("线程一"){			
		public void run() {
			//使用父类方法设置线程名称
			super.setName("修改线程名");
			//获取线程名称
			System.out.println(this.getName());
		}
	}.start();
2.当前线程对象
	new Thread(new Runnable() {
		public void run() {
			//使用Thread类静态方法currentThread()获取当前线程对象
			System.out.println(Thread.currentThread().getName());				
		}
	}).start();

三.线程状态

1.睡眠等待sleep

线程睡眠:当前线程等待指定时间后继续执行线程

	try {
		//当前线程睡眠1000毫秒后再继续执行
		Thread.sleep(1000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
2.守护线程|后台线程daemon

守护线程:当其他非守护线程全部执行完毕后,守护线程会马上终止执行

	Thread t= new Thread(){
		public void run(){
			try {
				
				Thread.sleep(3000);
				//程序不会执行到这里,因为该线程被设置为守护线程,当其他非守护线程执行完毕,该线程会立刻被终止
				System.out.println(this.getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
	//设置t为守护线程,setDaemon()必须先start()前调用
	t.setDaemon(true);
	t.start();
			
	//判断t线程是否为守护线程
	System.out.println(t.isDaemon());
	System.out.println("程序结束了");
	
	//输出结果:
	//			true
	//			程序结束了
	
	//main主线程终止了,不存在其他非守护线程,所以守护线程t立刻被终止
3.阻塞线程join

阻塞线程:子线程阻塞主线程,使主线程暂停执行,等子线程执行一段时间或执行完毕后再恢复执行主线程

	public static void main(String[] args) throws InterruptedException {
		//t线程在main线程中执行,所以main相当于主线程,t线程相当于子线程
		Thread t=new Thread(){			
			public void run() {
				for(int i=0;i<1000;i++){
					System.out.println(getName()+"...."+i);
				}
			}
		};
		

		t.start();

		//阻塞main主线程暂停执行,等待子线程t执行10毫秒后,主线程和子线程再同时继续执行
		//当参数为0或不填写时,会等待子线程执行完毕后才会恢复主线程的执行
		t.join(10);
		//大概在中间输出这句话,如果没有t.join(10),该行内容会输出在第一行
		System.out.println("==============程序结束=================");
	}
4.线程优先级

线程优先级:优先级越高的线程CPU会优先执行

	class MyThread extends Thread{			
		public void run() {
			for(int i=0;i<1000;i++){
				System.out.println(getName()+"...."+i);
			}
		}
	}
	
	Thread t1=new MyThread();
	Thread t2=new MyThread();
	
	//设置线程优先级,范围是MIN_PRIORITY~MAX_PRIORITY,即1~10
	//优先级越高的线程,CPU会优先执行
	t1.setPriority(Thread.MAX_PRIORITY);
	t2.setPriority(Thread.MIN_PRIORITY);
	
	t1.start();
	t2.start();
	
	//获取线程的优先级
	System.out.println(t1.getPriority());
	System.out.println(t2.getPriority());
5.礼让线程yield

礼让线程:暂停当前线程,尽可能让其他线程优先执行,该操作也可能无效

四.线程同步

当多个线程同时读写一个数据时可能会导致数据不一致,所以需要一个同步区域解决这个问题。同步区域在同一个时间只能被一个线程访问,当该线程访问完毕,其他线程才能访问同步区域。

同步锁:相同同步锁(对象引用地址一样,与对象属性值是否改变无关)的同步区域会相互排斥

1.同步代码块
	//局部内部类访问外部变量需要使用final修饰
	final Object obj=new Object();
	
	new Thread(){
		public void run() {
			//同步锁,锁对象为obj
			synchronized (obj) {
				try {
					//延时3秒
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("线程一");
			}
		};
	}.start();
	
	
	new Thread(){
		public void run() {
			//同步锁,锁对象为obj
			synchronized (obj) {
				System.out.println("线程二");
			}
		};
	}.start();
	
	//输出结果:
	//			(等待3秒)
	//			线程一
	//			线程二
	
	//两个线程的同步代码块都是同样的锁对象,所以这2个方法同时只能被一个线程访问
	//因为线程一先执行,所以线程二需要等待线程一的同步代码块执行完毕,才能执行线程二的同步代码块
2.同步方法
  • 同步成员方法同步锁对象是本类实例this
  • 静态方法同步锁对象是字节码对象
class MyClass{
	//1.同步成员方法同步锁对象是本类实例this
	public synchronized void print1(){
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(1);
	}
	
	
	public synchronized void print2(){
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(2);
	}
	
	//2.静态方法同步锁对象是字节码对象MyClass.class
	public synchronized static void print3(){
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(3);
	}
	
	
	public synchronized static void print4(){
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(4);
	}
3.死锁

同步代码相互嵌套时,使用相同的锁对象,可能会出现死锁

	final Object obj1=new Object();
	final Object obj2=new Object();
	new Thread("线程一"){
		public void run() {				
			synchronized (obj1) {
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				synchronized (obj2) {
					
				}
			}
		};
	}.start();
	
	new Thread("线程二"){
		public void run() {	
			synchronized (obj2) {
				synchronized (obj1) {
					
				}
			}
		};
	}.start();
	
	//死锁分析:
	//线程一进入obj1同步块,等待3秒
	//在线程一等待3秒期间线程二进入obj2同步块
	//线程二准备obj1同步块,但发现被线程一占用,所以等待
	//线程一等待3秒结束,准备进入obj2同步块,但发现被线程二占用,所以等待
	//线程一和线程二相互等待,程序无法向下执行,形成了死锁

五.线程通信

线程通信:当前线程唤醒其他线程的等待状态

1.Object方式
public class Test1 {
	public static void main(String[] args) {
		final MyClass mc = new MyClass();

		new Thread("线程一") {
			public void run() {
				try {
					//延时100毫秒,为了让所有线程都开启成功后再执行print1方法
					Thread.sleep(100);
					mc.print1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();

		new Thread("线程二") {
			public void run() {
				try {
					mc.print2();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();

		new Thread("线程三") {
			public void run() {
				try {
					mc.print3();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();

		new Thread("线程四") {
			public void run() {
				try {
					mc.print4();

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();
		
		//输出结果:
		//		我是方法2
		//		我是方法3
		//		我是方法4
		//		1
		//		2
		//		4
		//		3
		
		//分析:
		//		1.线程一延时了100毫秒,所以线程二丶三丶四会先输出语句
		//		2.线程二丶三丶四都执行了wait()进入被唤醒状态
		//		3.线程一开始输出1
		//		4.线程一调用notify()随机唤醒线程二丶三丶四中的一个线程
		//		5.本次运行结果唤醒的是线程二,所以继续执行wait()后面的代码,输出2
		//		6.线程二最后调用了notifyAll(),所以唤醒了所有为被唤醒的线程(线程三丶四)
		//		7.线程三丶四分别执行后面的代码,输出4,输出3
	}
}

class MyClass {
	/*
	 * 执行流程概述:
	 * 1.print1()执行完随机唤醒一个线程
	 * 2.其他方法都会先wait()等待,当被唤醒后会执行notifyAll()把其他线程都唤醒
	 * */
	
	public synchronized void print1() throws InterruptedException {
		System.out.println(1);
		//Object方法1:notify()
		//随机唤醒一个未被唤醒的线程
		this.notify();
	}

	public synchronized void print2() throws InterruptedException {
		System.out.println("我是方法2");
		
		//Object方法2:wait()
		//wait()线程立刻等待,到这行代码停止执行,并释放锁		
		this.wait();
		//当被唤醒后执行wait()后面的代码,不是重新执行print2()方法		
		System.out.println(2);
		
		//Object方法3:notifyAll()
		//唤醒其他所有在未被唤醒的线程
		this.notifyAll();

	}

	public synchronized void print3() throws InterruptedException {
		System.out.println("我是方法3");
		this.wait();
		System.out.println(3);
		this.notifyAll();
	}

	public synchronized void print4() throws InterruptedException {
		System.out.println("我是方法4");
		this.wait();
		System.out.println(4);
		this.notifyAll();
	}

}
2.ReentrantLock与Condition
public class Test3 {
	public static void main(String[] args) {
		final MyClass2 m = new MyClass2();
		new Thread() {
			public void run() {
				for (int i = 0; i < 3; i++) {
					try {
						m.print1();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();

		new Thread() {
			public void run() {
				for (int i = 0; i < 3; i++) {
					try {
						m.print2();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();

		new Thread() {
			public void run() {
				for (int i = 0; i < 3; i++) {
					try {
						m.print3();
					} catch (InterruptedException e) {

						e.printStackTrace();
					}
				}
			};
		}.start();
		
		
		//输出结果:
		//		1
		//		2
		//		3
		//		1
		//		2
		//		3
		//		1
		//		2
		//		3
		
	}

}

class MyClass2 {
	private int flag = 1;
	//1.创建ReentrantLock对象
	private ReentrantLock r = new ReentrantLock();
	//2.从同一个锁中获取多个条件,不同条件用于区分不同线程,执行各自线程的等待和唤醒
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();

	public void print1() throws InterruptedException {
		//3.获取锁
		r.lock();
		// 使用try-finally保证锁能被释放
		try {
			if (flag != 1) {
				//4.条件1的线程等待
				c1.await();
			}

			System.out.println(1);
			flag = 2;
			//5.唤醒条件2的线程
			c2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//6.释放锁
			r.unlock();
		}
	}

	public void print2() throws InterruptedException {
		r.lock();
		try {

			if (flag != 2) {
				c2.await();
			}

			System.out.println(2);
			flag = 3;
			c3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			r.unlock();
		}
	}

	public void print3() throws InterruptedException {
		r.lock();
		try {
			if (flag != 3) {
				c3.await();
			}

			System.out.println(3);
			flag = 1;
			c1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			r.unlock();
		}
	}
}

六.线程组

线程组:把线程进行分组,方便批量操作

	//1.创建线程组
	ThreadGroup tgA=new ThreadGroup("A组");
	ThreadGroup tgB=new ThreadGroup("B组");
	
	//2.在创建线程时指定所属线程组
	Thread t1=new Thread(tgA,"线程一"){			
		public void run() {			
			
			System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
		}
	};
	
	Thread t2=new Thread(tgA,"线程二"){			
		public void run() {
			System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
		}
	};
	
	class MyRunnable implements Runnable{
		public void run() {
			//获取当前线程对象
			Thread t=Thread.currentThread();
			//3.根据线程对象获取所属线程组对象
			ThreadGroup tg=t.getThreadGroup();
			//4.获取线程组名
			String groupName=tg.getName();
			System.out.println(groupName+"----"+ t.getName());
		}			
	}
	
	//5.在创建线程时指定所属线程组
	Thread t3=new Thread(tgB,new MyRunnable(),"线程三");
	Thread t4=new Thread(tgB,new MyRunnable(),"线程四");
							
	t1.start();
	t2.start();
	t3.start();
	t4.start();
	
	//6.获取线程组最高优先级
	System.out.println(tgA.getMaxPriority());
	//7.判断此线程组是否守护线程组
	System.out.println(tgB.isDaemon());

七.线程池

线程池是存放多个线程的容器,当需要使用线程时直接从线程池中获取,当线程执行完毕不会被销毁而是放回线程池中,这样避免了大量重复创建销毁线程的消耗,提高性能

1.Runnable
	Runnable r1=new Runnable(){
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println(Thread.currentThread().getName()+"-a");
		}			
	};
	
	Runnable r2=new Runnable(){
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-b");
		}			
	};
	
	Runnable r3=new Runnable(){
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-c");
		}			
	};
	
	//1.创建容量为2的线程池
	ExecutorService es= Executors.newFixedThreadPool(2);
	
	//2.提交三个任务
	es.submit(r1);
	es.submit(r2);
	es.submit(r3);
	
	//3.结束线程池
	es.shutdown();
	
	//输出结果:
	//		(等待一秒)
	//		pool-1-thread-1-a
	//		pool-1-thread-2-b
	//		(等待一秒)
	//		pool-1-thread-1-c
			
	//分析:
	//		1.线程池中只有2个线程,所以同时只能开启线程,执行2个任务
	//		2.线程a和b会先执行,线程c需要等待
	//		3.线程a和b执行完毕后,把两个线程放回线程池
	//		4.线程c从线程池中获取线程执行
2.Callable
	//1.Callable相对于Runnable线程任务可以有返回值
	Callable<Integer> c1=new Callable<Integer>() {
		public Integer call() throws Exception {
			Integer sum=0;
			for(int i=0;i<100;i++){
				sum+=i;
			}
			return sum;
		}
	};
	
	Callable<String> c2=new Callable<String>() {
		public String call() throws Exception {
			Thread.sleep(3000);
			return "abc";
		}
	};
	

	//2.创建单个线程的线程池
	ExecutorService es= Executors.newSingleThreadExecutor();
	
	//3.提交Callable任务有返回值
	Future<Integer> f1=  es.submit(c1);
	Future<String> f2=es.submit(c2); 
	
	Thread.sleep(1000);
	
	//4.判断任务是否已完成
	if(f1.isDone()){
		//5.获取任务的返回值
		System.out.println("任务一返回结果:"+f1.get());
	}
	
	//6.尝试取消任务,需要在任务执行完成之前取消,否则会返回false
	f2.cancel(true);
	
	//7.判断任务是否被取消
	if(f2.isCancelled()){
		System.out.println("任务二已取消");
	}
			
	System.out.println(f2.isCancelled());
	
	//结束线程池
	es.shutdown();

八.定时器Timer

	//1.定时器任务
	TimerTask tt=new TimerTask() {			
		public void run() {
			System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));				
		}
	};
	
	//设置日期
	Calendar c=Calendar.getInstance();
	c.set(2018,7,5,13,24,15);
	Date d=c.getTime();
	
	//2.定时器
	Timer t=new Timer();
	
	//3.在指定日期定时执行任务
	//	第二个参数是首次执行任务的时间
	//		如果当前时间小于首次时间,则等待达到了首次执行开始执行任务
	//		如果当前时间大于首次时间,会马上就执行任务	
	//	第三个参数是任务重复执行的间隔,单位毫秒,不填写任务只执行一次
	t.schedule(tt, d);
	
	Thread.sleep(1000*30);
	
	//4.取消定时器
	t.cancel();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值