黑马程序员----十三-多线程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

==========================================
13-01-08
==========================================

1.线程的状态
run方法结束,线程的任务结束了,也可以调用stop()方法来强制结束.

2.冻结
这个线程不在运行,但是还存活.调用sleep(time)方法.必须指定时间.时间到了就自动醒了.
也可以用wait()方法,再调用notify()唤醒.

3.运行状态的线程有cpu的执行资格,还有cpu的执行权.
  冻结状态的线程释放执行权和执行资格.
  临时阻塞状态就是具备执行资格,但是正在等待执行权.
  冻结状态的醒来后有可能是运行状态也可能是临时阻塞状态.

==========================================
13-01-09
==========================================

回顾:先1.run(),再2.start(),肯定是先执行完1,才会执行2.
     创建线程的第一种方式:继承Thread类

1.继承Thread类后,如果类中是别的方法,不用改名字也可以,直接把方法放到run()方法里面就行了.

2.如果子类要继承Thread类怎么办?
  如果让父类继承Thread,那么整个体系都变成了线程体系.
  解决:如果要把一部分代码变成线程任务,但是现在又有父类,那么就要扩展Demo的功能.但是现在有了父类了,那么就只能实现接口.
  准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行.通过接口的形式完成.

3.实现的话,有没有提供接口呢?打开API查,在java.lang中,有Thread,中有run()方法,中有接口Runnable,Runnable其实本身就是java.lang包中的一个接口.
  Runnable接口应该由那些打算通过某一线程执行其实例的类来实现.类必须定义一个run()方法.

4.Demo implements Runnable,只要把想在线程运行的代码放到run()方法里面就可以了.
  但是Demo还不是Thread线程对象,所以不能开启start(),如何开启?
  Demo d=new Demo();
  Thread t1=new Thread(t);
  t1.start();
  这就是创建线程的第二种方式:实现接口
  1.定义类实现Runnable接口
  2.覆盖接口中的run方法,将线程的任务代码封装到run方法中.
  3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递
  为什么?
  因为线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建时就必须明确要运行的任务.
  4.调用线程对象的start方法开启线程.

==========================================
13-01-10
==========================================

1.疑问:两个run方法,怎么运行的?
  原理:
  一个Thread(){}
  还有Thread(Runnable r){  this.r=r;  }
  提供了两个
  一public void run{  r.run();  }
  二public void start{  run();  }

==========================================
13-01-11
==========================================

1.实现Runnable的好处,不用继承所有功能.它的出现仅仅是将线程的任务进行了对象的封装.

2.Runnable r=new Student();
  Thread t=new Thread(r);
  t.start();

3.实现Runnable接口的好处
  1将线程的任务从线程的子类中分离出来,进行了单独的封装.
     按照面向对象的思想将任务封装成对象.
  2避免了java单继承的局限性.
    
  所以,创建线程的第二种方式较为常用

==========================================
13-01-12
==========================================

1.需求:卖票
  用4个窗口卖票.互相不干扰,所以用多线程.

2.因为几个线程共用一个数据,所以num可以设置成静态.
 
3.为什么有些数字会在1后面,因为已经计算过了,但是没有打印出来,1先打印出来了.

4.如果不用静态,有什么办法
  就不要继承Thread了,就实现Runnable接口,
  Ticket t=new Ticket();
  Thread t1 t2 t3 =new Thread(t1 t2 t3);
  t1.start();
  t2.start();
  t3.start();

5.如果是两个窗口卖100,另两个窗口卖100,
  Ticket t=new Ticket();
  ticket tt=new ticket();
  th t1=new th(t);
  th t2=new th(t);
  th t3=new th(tt);
  th t4=new th(tt);

==========================================
13-01-13
==========================================

1.假设现在num=1,四个线程都已经进来了,线程0满足num>0,进来了,现在cpu切换了,处理线程1了,然后线程1也进来了,输出的时候就会出现票剩下-1的情况.还可能出现-1,-2,-3.
  这样就出现了安全问题.

2.要调试出这种情况,就在输出语句之前使用sleep方法来暂停一下,sleep(long millis),加入一个Thread.sleep(10)也就是10毫秒,sleep方法里有一个异常声明,InterruptedException,是中断异常,所以catch这个异常,因为是一个中断异常,所以只能用catch,所以
try{Thread.sleep(10);}
catch{InException e)}{}

==========================================
13-01-14
==========================================

1.安全问题产生的原因.
  1多个线程在操作共享的数据
  2操作共享数据的线程代码有多条

2.当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生.

==========================================
13-01-15
==========================================

1.把if(num>0)和输出语句封装成一个整体.
 
2.解决思路:
  将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算.

3.通常封装是用{}大括号,再加个标识,synchronized.

4.在java中,用同步代码块就可以解决这个问题,同步代码块的格式是
synchronized(对象)
{
需要被同步的代码;
}
对象代表什么,相当于一个标记,Obj一个obj,放在括号里,如果有线程3,说明0,1,2都已经建立了.

==========================================
13-01-16
==========================================

1.4个线程进来了,线程0如果进来了,线程1就进不来,就算碰到了sleep,cpu切换到了其他线程,其他线程还是进不来,线程0执行完之后,线程1才进来.所以obj就相当于一个锁一样,控制线程进出.就叫同步锁.

2.同步的好处:解决了线程的安全问题.
  同步的弊端:相对降低了效率.因为同步外的线程都会判断同步锁.

3.同步加完之后安全问题依然存在,怎么办
  同步的前提:
  必须有多个线程并使用同一个锁.

==========================================
13-01-18
==========================================

1.别都放在同步代码块里面,会出问题的.

2.如果让函数具备同步性,就同时解决了封装和同步的问题.
  所以把同步关键字synchronized作为函数修饰符就可以了.
  public synchronized void add(int num)
  这就称之为同步函数.
  这是同步的第二种表现形式,第一种是同步代码块.

3.同步代码块有同步锁,但是同步函数的锁是什么呢?

4.一个在同步函数里,一个在同步代码块里,如果两个用的是一个同步锁,那么就是同步的,如果用的不是一个同步锁,那就不是同步的.

5.定义一个布尔 boolean flag=true;
t1.start();
t.flag=false;
t2.start();
这样两个线程就分开了

6.主线程开启了0线程之前,可以把主线程停一下,在t.flag之前插入
try{Thread.sleep(10);}
catch{InterruptedException e}{}

7.同步函数使用的锁是this.
  同步函数和同步代码块的区别:
  1同步函数的锁是固定的this(当前的对象),同步代码块的锁是任意的对象.开发的时候用同步代码块比较多.

==========================================
13-01-19
==========================================

1.睡眠的格式
try{
Thread.sleep(10);
}
catch(InterruptedException e)
{}
放在synchronized上面比较好,这样就可以休息10毫秒然后cpu切到其他线程的概率就非常大.

2.把函数给同步了,那么进了这个函数,其他线程就进不来.其他线程是可以得到执行权的,但是进不来.

3.验证同步函数的锁是哪一个.
0线程和1线程都有,但是都是在函数中执行的,t1.start();t.flag=false;t2.start();三句话,cpu在有执行权的时候,一下子就全部执行了,所以开启两个start的时候,flag就已经是false了,所以会发现连0线程的也都是在同步函数show里面执行的.
  那么就在t.flag=false;之前让主线程睡眠一下,t1的start就会用true来执行了.
  执行结果是0线程全是obj,1线程全是function,如果他们是同步的,就不会出现安全问题,如果没同步,就会出现安全问题.结果是有安全问题的,说明他们是不同步的.
  所以同步函数和同步代码块用的锁是不一样的.同步函数用的锁是this.

==========================================
13-01-20
==========================================

1.同步函数可以使用static静态
  静态同步方法当中根本没有this.这个函数进内存的时候只有一个当前的字节码文件对象.
  this.getClass()

2.Class c1=t.getClass();
  Class c2=Ticket.class;
  两种都行.

3.静态的同步函数使用的锁就是该函数所属的字节码文件对象,这个对象可以用getClass或者当前类名.class获取.只要保证多个线程使用的是同一个同步锁(对象)就行.

==========================================
13-01-21
==========================================

1.多线程下的单例
  饿汉
  private static final Single s=new Si();
  private Single(){}
  public static Single getInstance()
 {  return s;  }

  懒汉
  private static Single s=null;
  private Single(){}
  public static Single getInstance()
  {
 if(s==null)
 s=new Single();
 return s;
   }

2.懒汉式是否有安全隐患
  线程0和线程1都通过判断s==null,所以创建了两个对象,所以有安全问题.所以加同步
  public static synchronized Single getInstance()

3.每次拿对象都要判断锁,所以效率低,getClass方法不是静态的,所以不能用.

4.每次都要判断锁,怎么提高效率,改成同步代码块,然后前面加if(s==null),以后s就不是null了,就不用再判断锁了.
加同步是为了解决安全问题,加判断是提高效率.

5.还是写饿汉简单.面试的时候都面懒汉式.

==========================================
13-01-22
==========================================

1.死锁的情况.
  常见情景只一:同步的嵌套.
 
2.用两个锁试一试
  在同步函数中加入同步代码块,同步代码块的锁用obj,同步代码块中用同步函数,同步函数的锁是this.

3.有可能中断,有可能马上中断,有可能交叉.
 
4.面试多线程的时候有可能要写一个死锁程序.

==========================================
13-01-疑问
==========================================

1.Object obj=new Object();放在run方法里面和放在run方法外面为什么区别这么大.

2.堆里面多了一个Ticket,两个变量,一个num,一个obj,4个线程.

3.Person p=new Person();
p.show();
p.show();
被两个线程执行,两个线程栈里面都有show方法,是同一个对象.地址是同一个.

4.如果run方法里,int x=10;那么每个线程栈里都有x=10;4个线程在同时执行,判断obj,0在用,1不能用.如果new了Object(),那么每个线程都有自己的obj.

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值