Java 多线程
首先要理解进程(Processor)和线程(Thread)的区别
线程:线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦” 击杀“提莫”,同时“赏金猎人”又在击杀“盲僧”,这就是由多线程来实现的。
实现线程的方法:
三种
- 继承线程类
public class xiancheng extends Thread{
public void run(){
//必须要写run
}
}
2.创建多线程-实现Runnable接口,类似于继承线程类,只是这里实现的是接口
public class Battle implements Runnable{
public void run(){
}
}
3.使用匿名类,继承Thread,重写run方法,直接在run方法中写业务代码
匿名类的一个好处是可以很方便的访问外部的局部变量。
前提是外部的局部变量需要被声明为final。(JDK7以后就不需要了)
public static void main(String[] args) {
Thread t1= new Thread(){
public void run(){
}
t1.start();
}
启动线程是start()方法,run()并不能启动一个新的线程
这种方法有个好处就是可以直接在方法里面写,而且方法里的参数可以直接使用,比较方便。
对于线程,每次只要调用start函数就可以开始线程了。
注意:
线程是同时进行的,或者说是随机顺序的,你不能确定这些开始的线程谁先开始,他们跑得顺序每次可能都不一样,也许有哪次这个先跑,其他次数就别的先跑了
。
常见线程方法
sleep 当前线程暂停 //注意,该方法仍会占用对象。
join 加入到当前线程中
setPriority 线程优先级
yield 临时暂停
setDaemon 守护线程
同步:
synchronized表示当前线程,独占 对象 someObject
当前线程独占 了对象someObject,如果有其他线程试图占有对象someObject,就会等待,直到当前线程释放对someObject的占用。
someObject 又叫同步对象,所有的对象,都可以作为同步对象
为了达到同步的效果,必须使用同一个同步对象
这里就是相当于独占了改对象,别的线程想要占有该对象,必须等待该线程把该同步块
执行完,避免了冲突产生安全问题
线程安全的类
如果一个类,其方法都是有synchronized修饰的,那么该类就叫做线程安全的类
同一时间,只有一个线程能够进入 这种类的一个实例 的去修改数据,进而保证了这个实例中的数据的安全(不会同时被多线程修改而变成脏数据)
比如StringBuffer和StringBuilder的区别
StringBuffer的方法都是有synchronized修饰的,StringBuffer就叫做线程安全的类
而StringBuilder就不是线程安全的类
线程安全类:
HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
HashMap可以存放 null
Hashtable不能存放null
区别2:
HashMap不是线程安全的类
Hashtable是线程安全的类
StringBuffer 是线程安全的
StringBuilder 是非线程安全的
所以当进行大量字符串拼接操作的时候,如果是单线程就用StringBuilder会更快些,如果是多线程,就需要用StringBuffer 保证数据的安全性
通过在eclipse中查看源代码可以得知:
ArrayList类的声明:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
Vector类的声明:
public class Vector extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
一模一样的~
他们的区别也在于,Vector是线程安全的类,而ArrayList是非线程安全的。
ArrayList是非线程安全的,换句话说,多个线程可以同时进入一个ArrayList对象的add方法
借助Collections.synchronizedList,可以把ArrayList转换为线程安全的List。
与此类似的,还有HashSet,LinkedList,HashMap等等非线程安全的类,都通过工具类Collections转换为线程安全的
死锁问题:
- 线程1 首先占有对象1,接着试图占有对象2
- 线程2 首先占有对象2,接着试图占有对象1
- 线程1 等待线程2释放对象2
- 与此同时,线程2等待线程1释放对象1
显然,线程与线程之间必须有交互,这样才能解决一些实际问题
这里解释一些交互的方法
this.wait()表示 让占有this的线程等待,并临时释放占有
this.notify() 表示通知那些等待在this的线程,可以苏醒过来了。
留意wait()和notify() 这两个方法是什么对象上的?
public synchronized void hurt() {
。。。
this.wait();
。。。
}public synchronized void recover() {
。。。
this.notify();
}
这里需要强调的是,wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。
因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。
wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。
notify() 的意思是,通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了。
notifyAll() 的意思是,通知所有的等待在这个同步对象上的线程,你们可以苏醒过来了,有机会重新占用当前对象了。
注意:Sleep 与 wait 不一样 ,sleep 会锁定对象, wait 不会
锁定对象后,其他线程要是想要访问对象就得等待,所以要看好锁定的时间。如果两次不同的锁中间也会断开