-
Notifications
You must be signed in to change notification settings - Fork 0
Description
线程的概念
cpu的执行资格
cpu的执行权
线程的生命周期
start()
创建--------运行---------休眠
I sleep(times)
stop()I notify()
I wait()
消亡
如何将多个线程操作同一个共享数据:
比如去一个银行存钱,操作的都是同一个银行,如果采用继承Thread类的方法,每new一个Thread的子类建造线程,都会出来一个银行,因为银行这个对象是封装在你创建的Thread子类里。
方法一:将银行对象静态化static,这样就变成共有数据。但是不利于后期创建其他对象,就这样static化了,万一后期需要用到银行,创建其他银行不需要共享呢?
所以有方法二:
将某一类用implements实现Runnable接口,而不是继承Thread
然后把这个类的子类对象作为“任务”扔到 Thread t1 = Thread(子类对象)里去,这样尽管创建了多个Thread线程对象t1.t2.t3....,但是操作的任务都只是一个。(所谓任务,就是线程任务,也就是run方法内的代码)
线程出现安全隐患的原因:
判断步数:
步数1.查看是否有共享数据
步数2.如果有,查看操作这个共享数据的代码是不是不只一条,如果有多条则有隐患(比如之前的一个ticket_num先判断是否>0再输出,这是2个操作,一个判断,一个输出,可能判断完了,票数是大于0,有票,但是其他线程获得了cpu执行权,判断完,票数--,再回来,输出的就可能是0票或者负票,错误,或者像银行存钱,先sum = sum+num存钱,再输出,这也是2个操作,可能会有隐患)
是否有多条线程操控同一个共享数据。(这句话很抽象,可以结合上面的)
同步锁的理解问题:
线程的任务中,如果出现了安全隐患,则需要用同步锁来解除隐患,但是初学者可能会有这么一种想法:就是用同步锁之前,任务是CPU随机处理的,可能一会是A线程,还没处理完就去处理B线程了,但是加了同步锁后,A线程没处理完出来,B线程就不能进去处理。
首先来一句总体的易懂解释:
就是同步锁的出现,仅仅是锁住有安全隐患的任务“部分代码”!
比如
public void add(int num){
synchronized (obj) {
sum += num;
System.out.println(Thread.currentThread().getName()+"浦发银行目前进账+"+sum);
}
System.out.println("这里还是add函数哦");
}我们用同步锁仅仅锁住了对于安全隐患点sum的2个操作,而任务函数的其他地方并没有锁,所以并不是执行完A线程的add任务再执行B线程,而是保证A线程执行到“sum的2个操作时不被干扰”!!!,等他执行完,你CPU随便执行其他线程,因为下面还有一个输出语句,所以这也算是把A线程执行一半就丢下去执行B线程了。
任务确实是CPU随机处理,而且一会是A线程,可能只处理一半,就跑到B处了,这句话是重点,因为如果A,B线程处理的是共享数据,同时共享数据在任务中被操作2次(eg:先赋值,后输出)就会有安全隐患,比如A先赋值,本来输出A的赋值数,可是却转到B线程,B赋值完再转回来,这样A线程就输出了B线程操作后的同一共享数据的值。这就是安全隐患,使用同步锁,可以保证A线程的任务环节之一:操作共享数据时不被打扰,其他环节,你想打扰就打扰,所以不存在变成“使用同步锁,多线程变成单线程了”,问题纠结的原因就在你同步代码块,封装的是任务函数部分代码,还是整个任务函数!
同步函数与同步代码块的锁区别
同步代码块的锁是自己定义放置的,任意的,一般为Object的对象obj
而同步代码块的锁是this,即哪个对象调用了用synchronized修饰的同步函数,他就是同步函数的锁。所以一般情况,同步代码块和同步函数的锁不一致,如果把同步函数的锁,即那个调用同步函数的对象放到同步代码块内,则两个锁一致,会同步。外部线程无法进入已经有线程进入的同步锁。
建议使用同步代码块,同步函数是同步代码块的简化,如果把锁选择用调用的对象,那么同步代码块就可以简化成同步函数。
静态同步函数的锁 : 是该函数所属字节码文件对象,可以用getClass方法获取(this.getClass()),也可以用当前类名.class获取
在TicketDemo.java文件中,Ticket t = new Ticket(); t调用了线程,t所属的类就是当前类,就用Ticket.Class获取
一般由synchronized修饰过的同步函数,都会持有this关键字,this也就是同步函数的锁,但是!由静态static修饰过的静态同步函数,不会有this关键字,那么它的锁是什么呢?可以确定的是,锁肯定是对象,不会是类或者其他,而static函数是类一加载就有的,不会有对象啊?其实,每一个class文件加载的时候,都会有一个Class.class文件加载,也就是都会有一个Class对象,所以,静态同步函数的锁,就是一开始的Class对象,如果想要用同步代码块和静态同步函数共用一个锁,则可以使用this.getClass()或者是该类名.class,比如Ticket.class,任何类名都有一个静态的属性,是小写class的属性,用类名.class可以获得他的字节码文件对象。2个都可以作为同步代码块的锁。(一个是this.getClass(),一个是类名.class)注意一个是大写的Class,一个是小写的class
多线程下的单例
//饿汉式
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
```java
//懒汉式
加入同步为了解决多线程安全问题。
加入双重判断是为了解决效率问题。
```java
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}0,1线程进来new了对象以后,s!=null ,后面的线程就可以不用判断锁,直接获得return s,所以加入同步为了解决多线程安全问题。加入双重判断是为了解决效率问题。