JAVA异常和多线程

1.异常

1.1 异常处理
Scanner input = new Scanner(System.in);
		 try {
			 System.out.print("被除数:");
			 int num1 = input.nextInt();
			 System.out.print("除数:");
			 int num2 = input.nextInt();
			 System.out.println(num1/num2);
		 }catch(InputMismatchException e){
			 //整数
			 System.out.println("请输入整数");
             //return;//还会执行finally
		 }catch(ArithmeticException e){
			 System.out.println("除数不能为零");
             //System.exit(0);//不会执行finally
		 }catch(Exception e) {
             //所有异常
			 System.out.println("异常");
         }finally{
              //最终执行的代码
			 System.out.println("finally");
         }
//多重catch子类在前父类在后
//finally:无论如何都会执行的代码,
//---可靠的资源释放---连接
//return 会让finally执行,return是结束当前方法,
//其他方法继续执行,需要释放其他资源
//System.exit(0):退出虚拟机,所有方法都不执行,没有资源可以释放

throws //声明异常

//final finally finalize
//先把异常捕获,然后抛出异常---业务异常

//异常
    try,catch,finally,throw,throws
    执行正常代码,捕获异常,释放资源,抛出异常,声明异常
1.2自定义异常
//类方法
public class UserEcp extends UserException{
	private String message;
    //名字为message可以异常输出,继承了父类
    /**
    父类方法
     public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
	}
    */
	public UserEcp(String message) {
		super();
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}
//执行代码
public class TestSex {
	public static void main(String[] args)  {
		try {
			ecp();
		} catch (UserEcp e) {
			e.printStackTrace();
		//System.out.println(e.getMessage());
		}
		System.out.println("结束");
	}
	public static void ecp() throws UserEcp{
		Scanner input = new Scanner(System.in);
		System.out.println("请输入性别:");
		char gender = input.next().charAt(0);
		if(gender=='男'||gender=='女') {
			Student s = new Student("coco", gender);
			System.out.println(s.toString());
		}else {
			throw new UserEcp("请输入正确的性别");
		}	
	}
}

2.多线程

2.1进程和线程
进程:
	独立的应用程序,有独立内存空间
线程:
	一个进程多个线程组合在一起

		Thread main = Thread.currentThread();//当前线程
		main.setName("主线程");
		System.out.println(main.getId());//线程编号
		System.out.println(main.getName());//线程名字
		main.setPriority(Thread.MAX_PRIORITY);//1-10  优先级范围是1-10,默认是5
		System.out.println(main.getPriority());//线程优先级
		//main.setDaemon(false);//自己创建的其他线程可以设为守护线程,但是线程不可以
		System.out.println(main.getState());//线程状态--RUNNABLE
		System.out.println(main.isAlive());//线程是否还活着
		System.out.println(main.isDaemon());//是否是守护线程
		System.out.println(main.hashCode());//hashcode
		//注意这个不是单线程程序,只能说只有一个主线程,还有很多守护线程
		while(true) {
			
		}

2.2 为什么要编写多线程
1.让cpu更好的工作

2.多线程可以简化编程模型
2.3 如何编写多线程程序
创建线程四种方案:
	继承类
		public class MyThread extends Thread{
            public void run() {
                for(int i=1;i<10;i++) {
                    System.out.println(Thread.currentThread()+"线程----"+i);
                }

            }
		}
		//
		public class TestMythread {
                public static void main(String[] args) {
                    MyThread m1 = new MyThread();
                    MyThread m2 = new MyThread();
                    m1.start();
                    m2.start();
                }
            }
	实现Runnable接口
    	public class MyThread implements Runnable{
                public void run() {
                    for(int i=1;i<10;i++) {
                        System.out.println(Thread.currentThread()+"线程----"+i);
                    }
                }
            }
    	继承Thread
        public class TestMythread {
            public static void main(String[] args) {
                MyThread mth = new MyThread();
                Thread th = new Thread(mth);
                th.start();
            }
        }
    
    
2.4线程的生命周期

​ 1.阻塞之后不是进到运行状态,而是就绪状态,等资源

​ 2.一个线程只能start一次,启动之后只能等消失

2.5线程调度
1.setPriority(1-10)通过设值来设置线程执行概率
2.Thread.sleep():线程休眠
3.线程礼让:Thread.yeild()
4.线程加入:th.join() :让这个线程执行完再执行被阻塞的线程
5.线程中断:
public class MyThread extends Thread{	
	public void run() {
		//任务是死循环,不断检测中断信号,别人发中断信号了,我们就结束循环
		while(true) {
			if(this.isInterrupted()==true) {//检测中断信号
				break;
			}
		}
	}
}
public class Test {	
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		MyThread th = new MyThread();
		th.start();
		System.out.println("是否中断?y/n");
		char choice = input.next().charAt(0);
		if(choice=='y') {
			//调用线程中断的代码
			th.interrupt();//中断线程---不能改变线程目前运行状态,只能改变线程的一个中断标记
		}
	}
}
守护线程:
	如果不是守护线程,run方法要执行完才能退出虚拟机
	是守护线程,其余的非守护线程执行完毕之后就会退出虚拟机
	serDaemon(true):设置线程为守护线程
2.6 Callable

1.计算一个包含一万个元素数组,多线程并行计算数组元素的和

方案一 Runnable

​ 缺陷:无返回值,不能抛异常

//任务类
public class ArrayThread implements Runnable{
	int[] nums;
	int sum = 0;
	public void run() {
		for(int i=0;i<nums.length;i++) {
			sum += nums[i];
		}
	}	
	//构造方法来接收任务数组
	public ArrayThread(int[] nums) {
		super();
		this.nums = nums;
	}
}
//循环计算,创建十个线程
public class Test2 {
	public static void main(String[] args) throws InterruptedException {
		int[] nums = new int[10000];
		for (int i = 0; i < nums.length; i++) {
			nums[i] = i + 1;// 1-10000-----50005000
		}
		int sum =0;
		int count= 10;
		Map<Thread, ArrayThread> map = new HashMap<Thread, ArrayThread>();
		for(int i=0;i<count;i++) {
			int[] num =new int[nums.length/count];
			for(int j=0;j<num.length;j++) {
				num[j]=nums[j+nums.length/count*i];
			}
			ArrayThread st = new ArrayThread(num);
			Thread th = new Thread(st);
			th.start();//启动会自动计算第i个数组的结果
			map.put(th,st);
		}
		for(Thread th:map.keySet()) {
			th.join();//效率取决与最慢的线程
			sum+=map.get(th).sum;
		}
		System.out.println(sum);
		
	}
}

方案二 Callable

//类方法
public class ArrayThread2 implements Callable<Integer>{
	int[] nums;
	public Integer call() {
		int sum = 0;
		for(int i=0;i<nums.length;i++) {
			sum += nums[i];
		}
		return sum;
	}
	public ArrayThread2(int[] nums) {
		super();
		this.nums = nums;
	}
	public ArrayThread2() {
	
	}
}

//计算
public class Test2 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		int[] nums = new int[10000];
		for (int i = 0; i < nums.length; i++) {
			nums[i] = i + 1;// 1-10000-----50005000
		}
		int sum =0;
		int count= 10;
		List<FutureTask> fts = new ArrayList<FutureTask>();
		for(int i=0;i<count;i++) {
			//分配任务
			int[] num =new int[nums.length/count];
			for(int j=0;j<num.length;j++) {
				num[j]=nums[j+nums.length/count*i];
			}
			ArrayThread2 st = new ArrayThread2(num);
			//把Callable伪装成Runnable
			FutureTask<Integer> ft = new FutureTask<Integer>(st);
			Thread th = new Thread(ft);
			th.start();
			fts.add(ft);
		}
		for(FutureTask<Integer> ft:fts) {
			sum+=ft.get(); //算完一个给一个,等到call方法执行
		}
		System.out.println(sum);
	}
}
2.7 线程安全

铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,请编写多线程程序来模拟这个卖票效果

i. 窗口001正在销售第1000张票

ii. 窗口001正在销售第999张票

iii. 窗口002正在销售第998张票

iv. 。。。

v. 窗口002正在销售第1张票

vi. 票已经销售完毕

线程任务:
	总票数-1,编号+1,窗口不断卖票
	使用同步代码块
	synchronized加锁
//方法类一
public class SaleTicke implements Runnable{
	private String name;
	int num = 10;
	int count =0;
	@Override
	public void run() {
		while(true) {
			synchronized (this) {
				if (num <= 0) {
					System.out.println(Thread.currentThread().getName()+"售罄");
					break;
				}
				num--;
				count++;
				System.out.println(Thread.currentThread().getName()+"正在销售第"+count+"张票,还剩"+num+"张");
			}
		}
	}
}

//方法类二,同步方法
public class SaleMethods  implements Runnable{
	private String name;
	int num = 10;
	int count =0;
	boolean flag = true;
	@Override
	public void run() {
		while(flag) {
			sale();
		}
	}
	public synchronized void sale() {
		synchronized (this) {
			if (num <= 0) {
				System.out.println(Thread.currentThread().getName()+"售罄");
				flag = false;
				return;
			}
			num--;
			count++;
			System.out.println(Thread.currentThread().getName()+"正在销售第"+count+"张票,还剩"+num+"张");
		}
	}
}

//执行方法
public class Test {
	public static void main(String[] args) {
		
		SaleTicke st = new SaleTicke();
//		SaleMethods st = new SaleMethods();
		Thread th1 = new Thread(st,"窗口001");
		Thread th2 = new Thread(st,"窗口002");
		Thread th3 = new Thread(st,"窗口003");
		th1.start();
		th2.start();
		th3.start();
	}
}

Lock加锁会导致效率降低
2.8 线程不安全
2.8.1 懒汉单例不安全
//懒汉单例模式
public class Student {	
	private Student() {}
	//private static Student s=new Student();//饿汉模式,同一个对象
	private static Student s = null;
	public static Student getInstance() {
		if(s==null) {
			s = new Student();
		}
		return s;
	}
}

public static void main(String[] args) {
		for(int i=1;i<=3;i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println(Student.getInstance());
				}
			}).start();
		}
}
2.8.2 ArrayList是线程不安全的
public class TestArrayList {
	
	public static void main(String[] args) throws InterruptedException {
		ArrayList<String> list = new ArrayList<>();
		Thread th1 = new Thread(new Runnable() {
			public void run() {
				list.add("a");
			}
		});
		Thread th2 = new Thread(new Runnable() {
			public void run() {
				list.add("b");
			}
		});
		Thread th3 = new Thread(new Runnable() {
			public void run() {
				list.add("c");
			}
		});
		th1.start();
		th2.start();
		th3.start();
		th1.join();
		th2.join();
		th3.join();
		System.out.println(list);
	}
}
会出现以下情况:
	[b,a]  [a,c],[null, c, a] [null,c] [null,null,c]  [b] ...

//
Vector保证线程安全是通过所有代码都加synchronized
CopyOnWriteArrayList:线程安全的类,集合需要高并发
Hashtable---synchronized
HashMap---为同步---不安全
ConcurrentHashMap---安全

2.9 死锁
死锁:当线程需要多个资源的时候,一个线程获取到一部分资源,另外一个线程获取到另外一部分资源,这时候就会产生死锁
案例:
//类方法
public class Dead implements Runnable{
	//设计两个资源
	Integer a;//第一把锁
	Integer b;//第二把锁		
	public Dead(Integer a,Integer b) {
		this.a = a;
		this.b = b;
	}	
	public void run() {
		synchronized (a) {
			System.out.println(Thread.currentThread().getName()+"获取到"+a);
			synchronized (b) {
				System.out.println(Thread.currentThread().getName()+"获取到"+b);
				System.out.println(a+"+"+b+"="+(a+b));
			}
		}
	}
} 
//执行方法
public class TestDead {	
	public static void main(String[] args) {
		Dead d1 = new Dead(1,2);//a=1,b=2
		Dead d2 = new Dead(2,1);//a=2,b=1
		new Thread(d1,"线程1").start();
		new Thread(d2,"线程2").start();
	}
}

2.10 线程池
2.10.1 池的概念
线程池:装线程的池子
连接池:装连接的
为什么需要线程池:
	线程最大的缺点就是:开启之后运行完就没了
2.10.2 自定义线程池的原理
自定义线程池
    Executor
ThreadPoolExecutor
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
    
/*
		   int corePoolSize,--------核心线程个数
           int maximumPoolSize,-----最大线程个数
           long keepAliveTime,------保持存活时间
           TimeUnit unit,-----------时间单位
           BlockingQueue<Runnable> workQueue---工作队列--任务队列
		 */
ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4));	     
		pool.execute(new Runnable() {
			public void run() {
				System.out.println(Thread.currentThread().getName()+"--执行任务");
			}
});

TreadFactory 
2.10.3 自动创建线程池
 1ExecutorService pool = Executors.newSingleThreadExecutor();//创建一个单线程的线程池
new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
//特点:只有一个线程,队列无限大
public class TestPool2 {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newSingleThreadExecutor();//创建一个单线程的线程池
		//队列无限大,当任务很多,队列一直放,就会导致内存溢出
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			int temp = i;
			pool.execute(new Runnable() {
				public void run() {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"--"+temp);
				}
			});
		}
	}
}
2.ExecutorService pool = Executors.newFixedThreadPool(10);//创建一个单线程的线程池
  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
//特点:核心线程=最大线程=n,队列无线
public class TestFix {
	public static void main(String[] args) {
		ExecutorService  pool = Executors.newFixedThreadPool(10);//创建一个单线程的线程池
		for (int i = 1; i <=1000; i++) {
			int temp = i;
			pool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName()+"----"+temp);
				}
			});		
		}
	}
}

3ExecutorService pool = Executors.newCachedThreadPool();   
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}  
//特点:核心线程为0,最大线程无线,60s回收无任务的线程,队列是同步队列(自身没有缓存功能)
public class TestCached {
	public static void main(String[] args) {
		ExecutorService  pool = Executors.newCachedThreadPool();//
		for (int i = 1; i <=1000; i++) {
			int temp = i;
			pool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName()+"----"+temp);
				}
			});		
		}
	}
}

4、定时执行的线程池
	public class TestSchedule {
	public static void main(String[] args) {
		ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
		//后台定时器--以固定的频率
		pool.scheduleAtFixedRate(new Runnable() {
			public void run() {
				System.out.println("重复执行的代码");
			}
			
		}, 2, 1, TimeUnit.SECONDS);//第一次等两秒,后面每秒执行run里面的代码
	}
}	
/**
 * 核心线程和最大线程指定,队列是可以按照任务等待时间排序,可以实现任务的循环调用	
 * @author Administrator
 *
 */
2.10.4 源码分析
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))//true代表核心线程以内
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);//false代表超过核心线程
        }
        else if (!addWorker(command, false))//开最大线程失败
            reject(command);
    }
核心点:new线程池,同时知道这些参数之间的关系
2.11 ThreadLocal
1-1000个时间需要输出来:一个时间需要单独用一个线程来完成输出。
/**
 * 第一种方式
 * @author Administrator
 *
 */
public class Test {
	public static void main(String[] args) {
		for (int i = 1; i <=1000; i++) {
			int temp = i;
			new Thread(new Runnable() {
				public void run() {
					Date date = new Date(temp*1000);
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					System.out.println(sdf.format(date));
				}
				
			}).start();
		}
	}
}

/**
 * 第二种:使用线程池来解决线程的开闭和开销
 * @author Administrator
 *
 */
public class Test1 {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for (int i = 1; i <=1000; i++) {
			int temp =i;
			pool.execute(new Runnable() {
				public void run() {
					Date date = new Date(temp*1000);
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					System.out.println(Thread.currentThread().getName()+"--"+sdf.format(date));
				}
			});
		}
		pool.shutdown();
	}
}

/**
 * 第三种:把格式化的过程放到工具类中
 * 问题sdf创建的太多了
 * @author Administrator
 *
 */
public class Test2 {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for (int i =1; i <=1000; i++) {
			int temp  =i;
			pool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName()+"--"+DateUtil.getDate(temp));
				}
				
			});
		}
		pool.shutdown();
	}
}
class DateUtil{
	public static String getDate(int i) {
		Date date = new Date(i*1000);
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(date);
	}
}

/**
 * 第四种
 * 把sdf设计为一个对象
 * @author Administrator
 *问题:线程不安全
 *一个类如果没有成员变量,这个类一定是安全的
 *做成成员变量就被所有的线程所共享,因此就不安全
 */
public class DateUtil2 {
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static String getDate(int i) {
		Date date = new Date(i*1000);
		return sdf.format(date);
	}
}

/**
 * 第五种:加锁
 * 加锁实现了安全
 * 问题: 所有的线程所有任务都会在格式化这行代码阻塞,效率会受到影响
 * @author Administrator
 *
 */
public class DateUtil3 {
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static String getDate(int i) {
		Date date = new Date(i*1000);
		String str=null;
		synchronized (DateUtil3.class) {
			str = sdf.format(date);
		}
		return str;
	}
}

/**
 * 第六种:使用ThreadLocal来做线程隔离,每个线程设置一个SimpleDateFormat
 * @author Administrator
 *
 */
public class DateUtil4 {
//	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() {
		protected SimpleDateFormat initialValue() {
			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		}
	};//创建一个匿名内部类
	public static String getDate(int i) {
		Date date = new Date(i*1000);
		String str=null;
		str = tl.get().format(date);
		System.out.println(Thread.currentThread().getName()+"--"+System.identityHashCode(tl.get()));
		return str;
	}
}

2.12 ThreadLocal源码分析
public T get() {
        //每个线程执行get方法的时候获取线程对象
        Thread t = Thread.currentThread();//获取当前线程对象----
        //获取ThreadLocalMap对象---入参是当前对象--根据当前线程获取一个map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //如果这个map里面有值,就从map中取给你
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //另外的渠道给你
        return setInitialValue();
}
getMap(t):
   ThreadLocalMap getMap(Thread t) {
        //Thread类:
        //ThreadLocal.ThreadLocalMap threadLocals = null;
        return t.threadLocals;//t-当前线程   threadLocals:ThreadLocalMap类型的成员变量
}
//setInitialValue
private T setInitialValue() {
        //这个方法是我们在new ThreadLocal对象的时候用了一个匿名内部类重写他
        //第一次map里面没有这个值的这个时候会调用这个这个方法,如果后面这个线程再来获取
        //会直接从map里面给他
        T value = initialValue();//调到我们重写的方法---sdf
        Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);//获取线程里面的map集合
        if (map != null) {
            map.set(this, value);//map已经有了,一定是已经放了其他的tl对象进去
        } else {
            createMap(t, value);//创建一个ThreadLocalMap  threadLocals           
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
}

void createMap(Thread t, T firstValue) {
       //当前线程的map集合在这里创建---键是ThreadLocal对象,值是sdf
        t.threadLocals = new ThreadLocalMap(this, firstValue);//this  tl
}

set方法:修改ThreadLocal里面的值
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
}

remove方法:删除键值对
  public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
}   

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值