实现多线程的两种方式
1.继承Thread
2.实现Runnable接口
多线程安全 与 不安全的问题
安全:多线程安全指的是多个线程操作共享的数据能保持数据的一致性(多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。)
不安全:多线程操作一块共享数据,导致数据不一致。
多线程不安全的解决方案:
- 消除共享数据:成员变量与静态变量多线程共享,将这些全局变量转化为局部变量,局部变量存放在栈,线程间不共享,就不存在线程安全问题产生的环境了。消除共享数据的不足:如果需要一个对象采集各个线程的信息,或者在线程间传递信息,消除了共享对象就无法实现此目的。
- 使用线程同步机制:给读写操作同时加锁,使得同时只有一个线程可以访问共享数据。如果单单给写操作加锁,同时只有一个线程可以执行写操作,而读操作不受限制,允许多线程并发读取,这时就可能出现不可重复读的情况,如一个持续时间比较长的读线程,相隔较长时间读取数组同一索引位置的数据,正好在这两次读取的时间内,一个线程修改了该索引处的数据,造成该线程从同一索引处前后读取的数据不一致。是同时给读写加锁,还是只给写加锁,根据具体需求而定。同步机制的缺点是降低了程序的吞吐量。
- 建立副本:使用ThreadLocal为每一个线程建立一个变量的副本,各个线程间独立操作,互不影响。该方式本质上是消除共享数据思想的一种实现
线程同步:
多个线程必须使用同一个锁。
线程锁:
同步函数的锁是 :this 当前对象 唯一
同步代码块的锁是 :任意对象
静态同步函数的锁是:class对象
2个常用的单列模式
class person {
static final value =new person();
private person(){}
public person getInstance(){
return value;
}
}
class person2{
static final value =null;
private person2(){}
public static person2 getInstance(){
if(value==null){
synchronized(person2.class){
if(value==null){
value=new person2();
}
}
return value;
}
}
死锁:
分别有A 锁 和 B锁
有线程a 和线程 b
分别有2个同步代码
a线程执行的同步代码
synchronized(A){
synchronized(B){
}
}
b线程执行的同步代码
synchronized(B){
synchronized(A){
}
}
有可能发生 a线程拿到A锁时候,b线程又拿到B锁,a线程无法拿到B锁 发生死锁。