一、线程的同步
1.什么是线程的同步
线程同步(线程安全):多个线程访问同一份资源的时候要考虑这个问题。
同步方法:
使用同步代码块:synchronized(要加锁的变量){作用的代码块范围}
// 子线程类
public static class SonThread extends Thread {
private Object account;
@Override
public void run() {
synchronized(account) {
account.addMoney(100);
}
}
}
被加锁部分,直到运行的线程结束后解锁。
synchronized(Object obj){}括号里面跟的是一个地址不变的object,如果对象地址变了,就解锁了。一般是将各线程的共享对象放入锁内。
注意:Integer、String等类型一变化就会生成新的对象,所以要特别注意能不能用来做锁。
各线程共享的对象放到synchronized(各线程共享的对象){}后面的括号里(即同步监视器),对于Runnable,共享的对象是Runnable(因为Runnable对象作为参数传入了各个线程的构造方法)。对于另外一种直接用Thread实现的方法,共享的是跟main()同级的类属性(对象)。
2.同步方法
即用synchronized关键字修饰的方法。与前文修饰代码块的方式比起来,同步方法更多用一些。同步方法无需指定同步监视器,同步监视器默认为this,也就是对象本身。
与前文代码块的方式不同,锁从run()方法移到了具体的同步方法上,也更合理一些。
public class Access {
private double money;
public synchronized void subMoney(double money) {
if (this.money > money) {
this.money -= money;
}
}
}
上面的同步方法相当于以下写法:
public synchronized void subMoney(double money) {
synchronized(this) {
if (this.money > money) {
this.money -= money;
}
}
}
3.死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁(同步监视器即同步方法锁定的this,或者同步代码块中的共享的对象),而且Java虚拟机并没有检测死锁的机制,也不会采取任何措施来处理死锁。
二、线程的并发协作
1.什么是线程的并发协作
在JVM中,线程的调度有一定的随机性,但是我们可以通过一些机制来保证线程的协调运作。
2.线程间通信
1)wait()方法
该方法导致当前线程等待。()里可以加时间也可以不加时间(不加时间会一直等下去)。直到其他线程调用该同步锁对象的notify()方法或者notifyAll()方法来唤醒该线程。
2)notify()方法
唤醒该同步锁对象上等待的某个线程,如果有多个线程在此同步锁对象上等待,则随机唤醒其中一个。(由于随机性,少用一些)
3)notifyAll()方法
唤醒在此同步锁对象上等待的所有线程。多个线程的协调通过他们共享的对象(即同步锁)的属性判断来进行。
4)注意
wait、notify、notifyAll三个方法必须被在同步代码中被调用,并且只能由同步锁对象来调用。
wait()和sleep()最重要的区别:wait()会释放同步锁对象。wait、notify、notifyAll三个方法是Object提供的方法,sleep()是Thread提供的静态方法。
三、线程池
1.为什么要用线程池
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存周期很短的线程时,更应该考虑使用线程池。
2.线程池的工作机制
Java1.4之后加入
线程池在系统启动是即创建大量的空闲的线程,程序将一个Runnable对象传给线程池,线程池就会调用一个线程来执行它们的run方法,当run方法执行完成后,该线程也不会死亡,而是再次返回线程池中称为空闲线程,等待执行下一个Runnabe对象中的run方法。
3.Java提供的线程池
a.调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池
b.创建Runnable实现类作为线程执行对象
c.调用ExecutorService对象的submit()方法来提交Runnable实现类对象,交给一个线程池中线程执行
d.在需要的时候调用shutdown()方法关闭线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
newFixedThreadPool.submit(new Runnable() {
@Override
public void run() {
// do something
}
});
newFixedThreadPool.shudown();
实际运用中,线程池常搭配工厂模式和单例模式使用。
三、ThreadLocal类
1. ThreadLocal类的作用
也是用来处理两个线程共享同一个变量的情况,和同步锁给共享数组上锁不一样,这个ThreadLocal类是复制很多个副本,每个线程一个副本。
2.什么时候用ThreadLocal
根据具体情况。如果多个线程之间需要共享资源,以达到线程之间的通信功能,就使用同步机制;如果需要隔离多个线程之间的共享冲突,则可以使用ThreadLocal。
3.常用方法
T get(); 返回此线程中的变量副本值
void remove(); 删除此线程局部变量中当前线程的值
void set(T t); 设置此线程局部变量中当前线程副本中的值
四、Java中的定时器
就是Timer类。通过调用不同的schedule方法,定义不同的执行时间。可以循环执行。
new Timer().schedule(TimerTask task, Date time);
常用方法:
schedule(TimerTask task, Date time):执行任务,执行时间。
schedule(TimerTask task, long delay):过long毫秒后执行。
schedule(TimerTask task, Date firstTime, long period):执行任务,执行时间,循环周期。
schedule(TimerTask task, long delay, long period):执行任务,多长时间后执行,循环周期。