Java并发总结(一)

#Java并发编程总结(一)
最近看了Java并发编程实战、并发编程的艺术、多线程编程核心技术,觉得有必要做个总结回顾,所以将陆续写几篇博客。

《Java多线程编程核心技术》
创建线程的方式:这里比较熟悉就不记录了。

###停止线程的方式:
首先先了解两个方法:
public static boolean interrupted() 测试当前线程是否已经中断,留意到此方法是静态方法
public boolean isInterrupted() 测试线程是否已经中断

异常法退出线程:

//MyThread类:
public class MyThread extends Thread {
    public void run(){
        super.run();
        try{
            for(int i=0;i<500000;i++)
            {
                if (this.interrupted()) {
                    System.out.println("线程已经是停止状态了,我要退出了");
                    throw new InterruptedException();
                }
                System.out.println("i="+(i+1));
            }
            System.out.println("我在for的下面,如果我输出,那么就证明线程没有被停止!");
         }
        catch (InterruptedException e){
            System.out.println("进入MyThread.java类run方法中的catch了");
            e.printStackTrace();
        }
    }
}

异常法还可以用return来代替,但是通常建议使用抛异常,因为这样线程停止事件可以传播(catch块往上抛),可以传递给线程池。

在线程沉睡中停止,会抛出异常并且清除状态值,使之变成false;

暴力停止线程stop()方法,该方法已经作废,因为暴力停止不给程序执行的机会,会导致一些清理性的工作(如关闭流)无法完成,如果线程是同步方法,还可能导致数据不一致的问题,因为线程执行到一半(数据改变到一半)就被中断。

###线程停止的方法:
thread.suspend 和 thread.resume 方法,使用很简单,下面分析被遗弃的原因:
#####原因一:独占
但线程在占用独占锁时,此时被停止,那么其他线程将无法进入,此处有坑:

//此方法是同步方法,如果线程在执行此方法时被停止,那么其他线程将无法打印。
System.out.println("a");

#####原因二:不同步
举例:线程一的任务是设置账号及密码,当设置完账号时,线程停止了,此时密码还没设置。线程二的任务是打印账号及密码,此时打印的账号和密码可能是不匹配的。

#####yield方法:放弃当前的CPU资源,将他让给其他的任务去占用CPU执行时间,但是有可能出现刚刚放弃,马上又获得CPU时间片的可能。简单来说,就是所有线程重新竞争时间片。

#####线程的优先级
线程的优先级具有以下特点:
1、继承性:A线程启动的B线程,则B线程的优先级与A是一样的

2、优先级具有规则性:高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。

3、优先级具有随机性: 优先级高的线程不一定每一次都先执行完
###对象及变量的并发访问

同步synchronized不具有继承性:父类的synchronized方法,子类重写后,不具有同步性。

synchronized的可见性保证:对一个监视器的解锁操作happen-before于该监视器的加锁操作,也就是说线程a释放锁后,其所做的更改会被写到主存中。

###线程间通信
1.wait/notify实现线程间的通信:
wait()方法是Object类的方法,该方法用来将当前线程置入"预执行队列"中,并且在wait()所在的代码行处停止执行,直到接到通知或者中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在从wait()方法返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException.

当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常。

wait(long) 方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。

2.生产者/消费者模式:
可以通过操作值操作栈来实现(还有阻塞队列等)

3.通过管道进行线程间通信:字节流
在Java的JDK中提供了4个类来使线程间可以进行通信:
PipedInputStream和PipedOutputStream
PipedReader和 PipedWriter

####join方法
方法join的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码,在join过程中,如果当前线程对象被中断,则当前线程出现异常。
#####join(long)方法和sleep(long)方法的区别
join方法在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。join内部使用的锁是线程对象。

####ThreadLocal类
和线程绑定的对象,每个线程往里存和取具有隔离性
解决第一次调用ThreadLocal类的get()方法返回值是null,怎么样实现第一次调用get()不返回null呢,也就是说让它具有默认值的效果,方法是继承ThreadLocal,并重写initialValue()方法。

类InheritableThreadLocal使用该类,可以在子线程中取得父线程继承下来的值,通过重写childValue(Object parentValue)方法,可以在继承的同时对值进一步的处理。

####Lock的使用
Lock和synchronized的区别:synchronized在使用notify()/notifyAll()方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现选择性通知。synchronized相当于整个Lock对象只有一个单一的Condition对象,所有线程,都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

####定时器Timer

Timer timer = new Timer(true);//设置Timer的线程为守护线程
/*
TimerTask是以队列的方式一个一个被顺序执行的,所以执行时间和预期时间不一致,因为前面的任务有可能消耗的时间比较长,
则后面的任务运行的时间也会被延迟
计划时间早于当前时间:提前运行
计划时间晚于当前时间:未来执行
*/
schedule(TimerTask task, Date time)
schedule(TimerTask task, Date time, long period)
TimerTask的cancel()方法的作用是将自身从任务队列中清除了
Timer.cancel()方法的作用是取消所有任务
schedule(TimerTask task, long delay)//以当前时间为基准,延迟delay时间,执行
schedule(TimerTask task, long delay, long period)//以当前时间为基准,延迟delay,周期性执行
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)//具有追赶性(追赶性即补充执行)
schedule(TimerTask task, Date firstTime, long period)//不具有追赶性

####单例模式与多线程
立即加载/饿汉模式:即方法未调用,实例已经被加载

延迟加载/懒汉模式:

	if (instance == null) {
		instance = new MyObject();
	}

在多线程中,不能保证单例,需要加同步,同步方案:
1.在整个代码上加同步锁,效率太低
2.针对某些代码进行同步,DCL双检锁机制

	if (instance == null) {
		synchronized(MyObject.class) {
			if (instance == null) {   //第二次检查(双检)
				instance = new MyObject();
			}
		}
	}

使用静态内置类实现单例模式

 public class MyObject {
	//内部类方式
	private static class MyObjectHandler {
		private static MyObject myObject = new MyObject();
	}
	private MyObject() {}
	
	public static MyObject getInstance() {
		return MyObjectHandler.myObject;
	}	
 }

也可以使用枚举,枚举和静态代码块的特性是相似的。
反射会破坏单例模式,因为反射可以将构造函数设置为公共的,反序列化破坏单例的原因也是因为底层使用的反射。

####拾遗增补
线程的状态:
NEW READY RUNNING TIMED WAITING WAITING BLOCKED TERMINATED

线程组:

	Thread newThread = new Thread(group, runnable);//将线程归到group中
	listGroup[] listGroup = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
	Thread.currentThread().getThreadGroup().enumerate(listGroup);//将当前线程的所属线程组下的线程组放到listGroup容器中
	//在实例化一个线程组x时如果不指定所属线程组,则x线程组自动归到当前线程对象所属的县城组中,也就是隐式地在一个线程组中添加一个子线程组
	//JVM的根线程组是system,再取其父线程组为null
	group.interrupt()//组内批量停止

####SimpleDateFormat非线程安全
使用单例的SimpleDateFormat类在多线程的环境中处理日期,极容易出现日期转换错误的情况。即多线程不安全。
正确使用方法:
每个线程单独使用SimpleDateFormat对象,可以使用ThreadLocal对象。

####线程中出现异常的处理

为t1线程对象设置异常处理器

	Thread t1 = new Thread();
	t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
		@Override
		public void uncaughtException(Thread t , Throwable e) {
			System.out.println("线程:" + t.getName() + "出现了异常:");
			e.printStackTrace();
		}
	}
	)

在Thread类,可以为所有线程设置异常处理器,Thread.setDefaultUncaughtExceptionHandler;

线程组 中异常的处理器,需要自定义线程组并重写void uncaughtException(Thread t, Throwable e) 方法;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值