关于CodeJava的学习笔记——7

一、线程

1、程序、进程、线程定义

        程序:一段保存在物理介质中的代码

        进程:在操作系统上运行起来的一段程序

        线程:程序运行时的一条独立的执行线索

多线程:就是让程序中拥有多条独立的执行线索,从而同一时间可以完成多项任务,服务于多个用户。在某些场景下,使用多线程可以提高效率,但是使用多线程的目的是为了可以同时做多件事,从而服务于多个用户。

2、线程的七大状态

新生                就绪                运行                消亡                阻塞                锁池                等待池

3、实现线程的三种方式

1、extends Thread        2、implements Runnable        3、implements Callable<T>

import java.util.concurrent.*;
public class TestThreadPool{
	public static void main(String[] args)throws Exception{
		//				     Executors.newSingleThreadExecutor();//单一实例的
		//                   Executors.newCachedThreadPool();//缓存机制的
		ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的
		ThreadOne t1 = new ThreadOne();
		es.submit(t1);
		ThreadTwo t2 = new ThreadTwo();
		es.submit(t2);
		ThreadThree t3 = new ThreadThree();
		Future<String> f = es.submit(t3);
		for(int i = 0;i<10;i++){
			System.out.println("做任何与t3返回值无关的其它业务");
		}
		es.shutdownNow();
	}
}

/*
	Callable接口的出现 修正了原本Runnable接口的两大不足
		1.run()被定义为void方法 执行结束无法返回数据
		2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{
	@Override
	public String call()throws Exception{
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第3种方式");
		}
		return "ETOAK VIVA";
	}
}
class ThreadTwo implements Runnable{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第2种方式");
		}
	}
}
class ThreadOne extends Thread{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第1种方式");
		}
	}
}

4、控制线程的四种方法

1、setPriority(int)        设置优先级

2、static sleep(long) :        使该线程休眠long毫秒

java.util.concurrent.TimeUnit.SECONDS.sleep(1);        使该线程休眠long秒

3、static yield():       让当前线程放弃时间片直接返回就绪  

4、join() :         当前线程邀请调用方法的线程优先执行

        线程类所有静态方法 不要关注谁调用方法,而要关注调用出现在谁的线程体当中。

        线程类所有主动进入阻塞状态的方法,都有throws声明,所以必须进行异常处理

5、设置一个守护线程

1、守护线程应当无限循环 以防止其过早消亡。

2、守护线程的设置必须早于启动,否则触发IllegalThreadStateException

3、守护线程应当具有较低的优先级,尽量不要让守护线程和核心线程抢夺时间片

public class TestSetDaemon{
	public static void main(String[] args){
		GYJJ gy = new GYJJ();
		gy.setDaemon(true);
		gy.start();
		// * : 守护线程应当具有较低的优先级别 以防止其与核心业务争抢时间片
		gy.setPriority(1);
		for(int i = 0;i<666;i++){
			System.out.println("西天取经上大路 一走就是几万里");
		}
	}
}
class GYJJ extends Thread{
	@Override
	public void run(){
		//*: 守护线程通常都是无限循环 以防止其过早消亡
		while(true){
			System.out.println("你这泼猴儿...");
		}
	}
}

6、关于JUC包

        java.util.concurrent => 并发包

        1> 高并发情况下更好的集合

ConcurrentHashMap                CopyOnWriteArrayList                CopyOnWriteArraySet

        2> 多线程高并发的场景下 某些常用工具:倒计时门闩

CountDownLatch

import java.util.*;
import java.util.concurrent.*;
/*
	1.如何实现线程 如何给线程布置任务 如何启动线程
	2.如何给线程设置名字 如何得到线程的名字 setName() + getName()
	3.单例模式 - 醉汉式(预先加载)
	4.run()方法调用的其它方法当中 如何得到当前线程是谁
	5.多线程高并发的场景下 应该选择哪种键值对(Map)集合 ConcurrentHashMap
	6.Map集合的基础操作 put() containsKey() get() keySet() forEach()
	7.分析处理数据的线程 如何邀请生产数据的线程优先执行   join()
	8.连环try
	9.CountDownLatch => 倒计时门闩			since JDK5.0
		构造方法: 传参(int)指定门上装多少个门闩
		核心方法:
			countDown() : 打开一个门闩
			await() : 主动制造阻塞 等待开门
*/
public class TestCurrentThread2{
	public static void main(String[] args){
		Student s1 = new Student("小翔");
		Student s2 = new Student("小俐");
		Student s3 = new Student("小黑");
		s1.start();
		s2.start();
		s3.start();

		try{
			X.latch.await();//主动阻塞 等待开门
		}catch(Exception e){
			e.printStackTrace();
		}



		//查账 收包子
		System.out.println("========= 开始查账 ==========");
		Teacher tea = Teacher.getOnly();
		{
			Map<Integer,String> temp = new TreeMap<>((a,b) -> a.equals(b) ? 1 : b.compareTo(a));
			tea.map.forEach((k,v) -> temp.put(v,k));
			temp.forEach((count,name) -> System.out.println(name + " : " + count));
		}
		System.out.println("========= 查账结束 ==========");
	}
}
class X{
	static CountDownLatch latch = new CountDownLatch(3);
}
class Student extends Thread{
	public Student(String name){
		setName(name);
	}
	@Override
	public void run(){
		System.out.println("我叫"+getName()+"学习遇到了问题 准备问老师");
		Teacher tea = Teacher.getOnly();
		int x = (int)(Math.random()*3)+3;//3-5
		for(int i = 0;i<x;i++){
			tea.hdwt();
		}
		X.latch.countDown();//打开一个门闩
	}
}
class Teacher{
	Map<String,Integer> map = new ConcurrentHashMap<>();
	private Teacher(){}
	private static Teacher only = new Teacher();
	public static Teacher getOnly(){
		return only;
	}
	public void hdwt(){
		Thread x = Thread.currentThread();
		String name = x.getName();
		System.out.println("认真的回答"+name+"同学的问题");
		//记账 记录name同学又多欠他一个包子
		if(map.containsKey(name)){
			Integer v = map.get(name);
			map.put(name,v+1);
		}else{
			map.put(name,1);
		}
	}
}




        3> OO思想实现的锁!

java.util.concurrent.locks.ReentrantLock

import java.util.concurrent.locks.*;
public class TestSwitchThreadWithLock{
	public static void main(String[] args){
		RightThread rt = new RightThread();//
		LeftThread lt = new LeftThread(rt);

		lt.start();//

	}
}
class X{
	static Lock lock = new ReentrantLock();
	static Condition cdt = lock.newCondition();//新建阻塞条件
}
class LeftThread extends Thread{
	RightThread rt;
	public LeftThread(RightThread rt){
		this.rt = rt;
	}
	@Override
	public void run(){
		X.lock.lock();
		rt.start();//
		for(int i = 0;i<666;i++){
			System.out.println("A玩家的一组操作");//1st.
			try{X.cdt.await();}catch(Exception e){e.printStackTrace();}//2nd.
			X.cdt.signal();//6th.
		}
		X.lock.unlock();

	}
}
class RightThread extends Thread{
	@Override
	public void run(){
		X.lock.lock();
		for(int i = 0;i<666;i++){
			System.out.println("				B玩家的一组操作");//3rd.
			X.cdt.signal();//4th.
			try{X.cdt.await();}catch(Exception e){e.printStackTrace();}//5th.
		}
		X.lock.unlock();

	}
}

        4> 线程池实现

ExecutorService   Executors  Future

import java.util.concurrent.*;
public class TestThreadPool{
	public static void main(String[] args)throws Exception{
		//				     Executors.newSingleThreadExecutor();//单一实例的
		//                   Executors.newCachedThreadPool();//缓存机制的
		ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的
		ThreadOne t1 = new ThreadOne();
		es.submit(t1);
		ThreadTwo t2 = new ThreadTwo();
		es.submit(t2);
		ThreadThree t3 = new ThreadThree();
		Future<String> f = es.submit(t3);
		for(int i = 0;i<10;i++){
			System.out.println("做任何与t3返回值无关的其它业务");
		}
		es.shutdownNow();
		System.out.println("===========================");
	}
}

/*
	Callable接口的出现 修正了原本Runnable接口的两大不足
		1.run()被定义为void方法 执行结束无法返回数据
		2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{
	@Override
	public String call()throws Exception{
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第3种方式");
		}
		return "ETOAK VIVA";
	}
}
class ThreadTwo implements Runnable{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第2种方式");
		}
	}
}
class ThreadOne extends Thread{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第1种方式");
		}
	}
}

7、懒汉式单例模式

public class TestSingleton{
	public static void main(String[] args){

	}
}
class Sun{
	private Sun(){}
	private static Sun only;//null
	public static synchronized Sun getOnly(){
		if(only == null)
			only = new Sun();
		return only;
	}
}

8、并发错误

        (1)定义

        多个线程共享操作同一份数据的时候,线程体当中连续的多行操作未必能够连续执行,很可能操作只完成了一部分,时间片突然耗尽,而另一个线程抢到了时间片,直接访问了操作不完整的数据。

        (2)如何解决并发错误

        语法内置的加锁: synchronized修饰符,能够修饰代码块、修饰整个方法,隔代丢失,只对当前类型有效。

		修饰代码块:
			synchronized(临界资源){
				需要连续执行的操作1;
				需要连续执行的操作2;
				....;
			}

		修饰方法:
			public synchronized void add(Object obj){
				需要连续执行的操作1;
				需要连续执行的操作2;
				...;
			}

        面向对象的加锁: ReentrantLock可重入锁

            lock()        unlock()
            加锁        释放锁

        它可以指定公平锁或非公平锁        new ReentrantLock(true);

9、死锁

        多个线程相互持有对方想要申请资源不释放的情况下,又去申请对方已经持有的资源,从而双双进入对方已经持有的资源的锁池当中,产生永久的阻塞

         如何解决死锁

        使用对象的等待池和操作等待池的wait() notify() notifyAll()

public class TestDeadLock{
	public static void main(String[] args){
		QCRoad qcl = new QCRoad();
		QCRoad.Benz s900 = qcl.new Benz();
		QCRoad.Bmw x9 = qcl.new Bmw();
		s900.start();
		x9.start();

	}
}
class QCRoad{
	Object east = new Object();		//路东
	Object west = new Object();		//路西

	class Benz extends Thread{
		@Override
		public void run(){
			System.out.println("孟总驾驶奔驰驶出家门去上课");
			synchronized(east){
				System.out.println("孟总和他的奔驰已经占领了泉城路东侧");
				try{java.util.concurrent.TimeUnit.SECONDS.sleep(1);}catch(Exception e){e.printStackTrace();}
				try{east.wait();}catch(Exception e){e.printStackTrace();}
				synchronized(west){
					System.out.println("孟总和他的奔驰又占领了泉城路西侧");
				}
			}
			System.out.println("孟总顺利的通过了泉城路");
		}
	}
	class Bmw extends Thread{
		@Override
		public void run(){
			System.out.println("高总驾驶宝马驶出家门去上课");
			synchronized(west){
				System.out.println("高总和他的宝马已经占领了泉城路西侧");
				try{java.util.concurrent.TimeUnit.SECONDS.sleep(1);}catch(Exception e){e.printStackTrace();}

				synchronized(east){
					System.out.println("高总和他的宝马又占领了泉城路东侧");
					east.notify();
				}

			}
			System.out.println("高总顺利的通过了泉城路");
		}
	}
}

10、线程池

        线程池是一种标准的资源池模式      

        资源池:是指在用户出现之前 提前预留活跃资源,从而在用户出现的第一时间,直接满足用户对资源的需求,并且将资源的创建和销毁都委托给资源池完成,从而优化用户体验       

        假如一个线程的完整执行时间为T         

        T = t1 + t2 + t3         

         t1: 在操作系统当中映射一个线程所消耗的时间        

        t2: 线程核心逻辑执行的时间 run()         

        t3: 在操作系统当中销毁一个线程所消耗的时间     

        如果run()当中代码非常简短 则t2所占T的比例就会很小,我们会感觉喧宾夺主 主次不分

import java.util.concurrent.*;
public class TestThreadPool{
	public static void main(String[] args)throws Exception{
		//				     Executors.newSingleThreadExecutor();//单一实例的
		//                   Executors.newCachedThreadPool();//缓存机制的
		ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的
		ThreadOne t1 = new ThreadOne();
		es.submit(t1);
		ThreadTwo t2 = new ThreadTwo();
		es.submit(t2);
		ThreadThree t3 = new ThreadThree();
		Future<String> f = es.submit(t3);
		for(int i = 0;i<10;i++){
			System.out.println("做任何与t3返回值无关的其它业务");
		}
		es.shutdownNow();
		System.out.println("===========================");
	}
}

/*
	Callable接口的出现 修正了原本Runnable接口的两大不足
		1.run()被定义为void方法 执行结束无法返回数据
		2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{
	@Override
	public String call()throws Exception{
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第3种方式");
		}
		return "ETOAK VIVA";
	}
}
class ThreadTwo implements Runnable{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第2种方式");
		}
	}
}
class ThreadOne extends Thread{
	@Override
	public void run(){
		for(int i = 0;i<666;i++){
			System.out.println("我是创建线程的第1种方式");
		}
	}
}

        如果自己创建一个线程池执行器,需要多少个参数,各自代表什么含义

        new ThreadPoolExecutor(1,2,3,4,5);

            1: 线程池当中 核心线程的数量
            2: 线程池当中 最大线程数量
            3: 保持活着的时间 KeepAliveTime    
            4: 时间单位 TimeUnit  
            5: 一个Queue (是一种集合)   

11、shutdown() 和 shutdownNow()的区别

        它们都能够禁止新任务再次提交,它们都不能够停止正在执行中的线程任务

        区别在于已经提交但是还没开始执行的线程任务

          shutdownNow() : 会被直接退回 无法执行

          shutdown() : 正常执行结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值