黑马程序员---线程1

来源:互联网 发布:python基础教程怎么样 编辑:程序博客网 时间:2024/06/07 15:18

----------------------------------------android培训  java培训 期待与您交流-------------------------------------------------


(1).进程与线程
 进程:是一个正在执行的程序,每个进程执行都有自己的执行顺序,这就叫程序的执行路径
 线程:就是进程中独立的控制单元,一个进程中可以有多个线程,这些线程共享进程的内存,线程控制进程的执行
 一个进程中至少有一个线程

 多线程之间互不干扰,一个之间出现异常不影响另一个线程的运行,它们各自都有自己的栈,各自管理自己的栈
 JVM在启动时会创建一个线程,这个线程执行main()方法中的代码,称为主线程

(2).创建线程的方法:
第一种方式:继承Thread类
 1:定义类继承Thread类
 2: 重写Thread中的run()方法;将线程要执行的代码放入run()方法中,线程运行是就运行run()方法中的代码
 3:调用start()方法;目的是启动线程,运行run()方法
 getName():返回线程对象的名称
 currentThread():返回正在执行的线程的名称
 
第二种方式:实现Runnable的接口
 1.定义实现Runnable的类
 2.覆盖Runnable接口中的run()方法
 3.通过Thread类建立线程对象
 4.将Runnable子类的对象传递给Thread类的构造函数
   因为自定义的run方法是Runnable接口的子类的对象,所以要让线程指定run的方法
 5.调用Thread类的start方法,开启线程

(3).实现和继承方法的区别:
 实现:避免了单继承的局限性,建立线程时建议使用这种方式
 继承方式中线程代码放在run方法的代码中,实现方式中线程代码放在实现Runnable接口的子类run()方法中
 继承Thread创建线程的弊端:
  无法再继承其它的类,因为继承了Thread类,java不支持多继承
  因为每个线程都有一个Thread子类的实例,所以多个线程之间共享数据比较麻烦
 
(4).CPU小常识:
 CPU执行资格:具备被CPU处理的资格,正在CPU处理的队列中排队
 CPU执行权:正在被CPU执行的权力
 程序执行过程:
  当程序被加载进内存后,操作系统就为程序赋予了执行资格并加上了一个标记,程序在进程的执行队
  列中排队,等待着操作系统执行程序;当程序运行完或系统分配给程序的时间片用完后,操作系统就会准备
  就绪的执行队列中调用程序进行执行,这样这个程序就具备了执行权明确一点,在某一个时刻,只能有一个
  程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的
  运行行为在互相抢夺cpu的执行权

 临时阻塞状态:具备执行的资格但不具备执行权,正在等待执行权
  静态同步方法和非静态同步方法不会彼此阻塞,因为静态方法同步锁定在class类文件上,非静态方法锁定
  在类的对象上
(4).注意点
 多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动,否则会抛出
     IllegaThreadStateException异常

*/

//第一种创建方式:
class Demo extends Thread{
 //public String name;
 Demo(String name){
  super(name);
  //this.name=name;
 }
 public void run(){
  for(int i=0;i<70;i++)
   System.out.println("demo running……"+i+"……"+Thread.currentThread().getName());
 }
}
public class ThreadDemo{
 public static void main(String[] args){
  Demo dd=new Demo("dd");
  Demo dd1=new Demo("dd1");
  dd.start();
  dd1.start();
  for(int i=0;i<60;i++)
   System.out.println("Thread running……"+i+"……"+Thread.currentThread().getName());
 }
}

//第二种创建方式:
class Demo implements Runnable{
 Object obj=new Object();
 public synchronized void run(){
  for(int i=0;i<10;i++){
   {
     System.out.println("demo run……"+Thread.currentThread().getName());
     System.out.println("hello");
     }
    System.out.println("word");
   }
  System.out.println("word");
 }
}
public class ThreadDemo{
 public static void main(String[] args){
 
  Demo dd=new Demo();
  Thread t=new Thread(dd);
  Thread t1=new Thread(dd);
  Thread t2=new Thread(dd);
  t.start();
  t1.start();
  t2.start();
  System.out.println(t.getName());
  System.out.println(Thread.currentThread().getName());
  System.out.println("word");
 }
}

 

//线程共享数据产生的问题
class Ticket2  implements Runnable{
 private String name;
 private int num=100;
 Ticket2(String name){
  this.name=name;
 }
 public void show(){
      if(num>0)
    System.out.println(Thread.currentThread().getName()+"--"+"Ticket:"+num--);
 }
 public void run(){
  while(true){
   show();
  }  
 }
}
public class TestTicket2{
 /*
  这个程序创建了三个线程,但三个线程共享一个任务对象;这样卖出的可以表示是种票
  但这样仍存在不安全因素;因为线程是的执行是随机切换的,如还有一张票的时候,这时线程1刚好做完if判断
  线程切换到线程2执行,这时线程2仍能仍能进入if语句里,因为线程1还没有执行num--操作,num还是在于0的;
  这样当线程1再次执行时,num就为成了1,当线程2再次执行时就输出了0,这样就将0张票给卖出去了,显然卖
  出0张票是不合理的
 */
 public static void main(String[] args){
  Ticket2 t=new Ticket2("tt");
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
 }
}

 


(1).多线程不安全的原因:
  多线程具有随机性,因为CPU在不断的快速的切换,有一个线程对多条操作共享数据的代码执行的一部分还
  没有执行完另一个线程开始参与执行,就会发生数据错误。
 
(2).多线程的不安全前提:
  线程代码是有操作共享的数据
  操作共享数据的代码有多条
 
(3).synchronized:同步关键字
 同步代码块:
  synchronized(对象){
   操作共享数据的代码
  }
 同步函数:
 synchronized 函数名 {
  函数代码
 }
(4).线程同步的前提:必须使用同一把锁
  任何线程在进入同步方法,同步代码块之前,必须先获取同步方法,同步代码块对应的同步监听器
  对于同步代码块,程序必须为它显式的指定同步监听器
  对于非静态的同步方法而言,该方法的同步监听器是this--即调用该方法的对象
  对于静态的同步方法,该方法的同步监听器不是this,而是类本身
 
(5).注意点
  对于并行执行同一个类的两个不同对象的同步方法并没有限制,只是控制对同一个对象的并发访问
  在同步时,应该注意尽量只同步对共享数据操作的语句
(6).多线程弊端:
  较为消耗资源
  同步嵌套后容易发生死锁
 
*/


class Ticket4 implements Runnable
{
 private  int tick = 100;
 public  boolean flag=true;
 Object obj = new Object();  //这只创建了一个锁,多个线程共用一把锁,能保证线程的同步
 public void run()
 {
  //Object obj = new Object();  //这就是每创建一个线程就创建了一个锁,这并不能保证线程的同步
  while(true)
   synchronized(obj)
   {
    if(tick>0)
    {
     try{Thread.sleep(10);}catch(Exception e){}
     System.out.println("------obj-"+Thread.currentThread().getName()+"---");
     System.out.println(tick--);
    }
   }
   //show();
  
 }
 public synchronized void show(){
  if(tick>0){
   System.out.println("-show-"+Thread.currentThread().getName()+"---");
   System.out.println("Ticket:"+tick--);
  }
 }
}
class  TestTicket4
{
 /*
  程序创建了一个任务对象,创建了四个线程任务,每个线程共用同一把锁
  当线程任务中只有一个同步代码块或同步方法时,比较好理解,但当线程任务中有多个同步代码块或同步方法时
  就不好理解了,这时我们要知道,每个线程都是运行run()方法中的内容,当所有的线程共用同一把锁时,它们
  争得的资源也就是锁,谁先得到锁谁就可以执行,当该线程释放锁后,所有线程又会再次争取锁
 */
 public static void main(String[] args)
 { 
  Ticket4 t = new Ticket4(); 
  Thread t1 = new Thread(t);
  Thread t2 = new Thread(t);
  Thread t3 = new Thread(t);
  Thread t4 = new Thread(t);
  t1.start();
  t2.start();
  t3.start();
  t4.start();
 }
}

(7).死锁:同步中嵌套同步
 多个线程并发执行,共同使用共享资源,会出相互拥有资源而无法用其他线程拥有的资源,又无法释放自己的资源导 致死锁
 
//死锁代码
class Test implements Runnable{
 public boolean flag=true;
 Object obj=new Object();
 public void run(){
  if(flag)
   synchronized(obj){
   while(true){
    System.out.println("if--obj--");
    show();
    }
   }
   else{
    while(true)
     show();
   }
  
 }
 public synchronized void show(){
  System.out.println("else--show---");
  //try{wait(30);}catch(Exception e){}
   synchronized(obj){
    System.out.println("show obj--");
   }
 }
 
 }
 
 public class Te{
 /*
  程序中创建了一个任务对象 ,两个线程任务;在同步代码中方法中嵌套了同步代码块,同步代码块中嵌套了同步
  方法;当两个线程在执行时,同步代码块取得锁并等待着取同步方法中的锁,而同步方法取得锁并等待着取同步
  代码块中的锁,这样两个线程都在等待着对方释放锁资源,从而造成二个线程都在等待状态,即为死锁
 */
 public static void main(String[] args){
  Test t=new Test();
 // Test t2=new Test();
  new Thread(t).start(); 
  new Thread(t).start();
   t.flag=false;
 // new Thread(t2).start();
 }
 }
 
(9).线程间的通信
  多个线程在处理同一个资源,但是任务却不同,多个线程之间通过某种通信方式来协调完成任务
 
(10).等待唤醒机制
  wait():让线程处于冻结状态,被wait的线程会被存储到线程中
  notity():唤醒线程池中一个线程(任意线程)
  notityAll():唤醒线程池中的所有线程
  以上的方法都是继承自Object对象,因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意的对象,
  任意的对象调用的方式一定定义在Object当中
 
注意:这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法中,必须要明确到底操作的是那个锁上的线程
 wait()要捕获异常,而notify(),notifyAll()这两个方法上的异常是运行时异常,不用捕获
 
(11).sleep()和wait()方法的区别
 sleep()是Thread中的静态方法,必须要有时间限制;当使用sleep()时会释放执行权,但是不释放锁
 wait()是Object中的方法,没有时间限制;当使用wait()时不仅会释放执行权还会释放锁

 
(12).线程常见的一些方法。
  setDaemon()将线程标记为守护线程
  join();等待该线程终止
  优先级  可以设置线程获取cpu执行权限的优先顺序
  yield();临时暂停线程
  在开发时,可以使用匿名内部类来完成局部的路径开辟。
  
(13).Lock:出现替代了同步代码块或者同步函数,将同步的隐式锁操作变成现实锁操作同时更为灵活,可以一个锁上加
  上多组监视器
  JDK1.5以后出现的,该接口将锁和同步封装成了对象;并将操作锁的隐式方法定义到了该对象中,将隐式动作
  变成了显示动作同时更为灵活,可以一个锁上加上多组监视器
 lock():获取锁
 unlock():释放锁,通常定义需要定义finally代码块中

 Condition接口:出现替代了object中的wait,notify(),notifyAll方法,将这些监视器方法单独进行了封装,
 变成Condition监视器对象,可以任意锁进行组合
 await():让线程等待
 signal():唤醒某个线程
 signalAll():唤醒所有线程

(14).停止线程的方法:
  stop()方法,已过时
  run()方法结束:  停止线程可以使用run方法,当线程没有了要运行的代码线程就结束了,意味着,任务结束,线程消失。一般情况下run方法中会定义循环,结束run方法,通过标记控制其中的循环就行了,如果读不到标记,线程就处于了冻结状态,可以用Thread类中的interrupt方法,清除线程中的冻结状态。
 
 

/*
 生产者消费者问题
 等待唤醒机制
*/
class Resource{
 private String name=null;
 private int count=0;
 private boolean flag=false;
 public synchronized void set(String name){
  while(flag)  //这里不能用if判断,因为if只判断一次而while是循环判断
   try{wait();}catch(Exception e){}
  this.name=name;
  count++;
  System.out.println("--生产商品--"+this.name+count+Thread.currentThread().getName());
  flag=true;
  notifyAll();//这里不能用notify(),因为notify()只唤醒线程池中的一个线程,在这个程序中会产生死锁
 } 
 public synchronized void out(){
  while(!flag)
   try{wait();}catch(Exception e){}
  System.out.println(Thread.currentThread().getName()+"--消费--"+this.name+count);
  flag=false;
  notifyAll();
 }
}
class Consumer implements Runnable{
 public Resource r=null;
 Consumer(Resource r){
  this.r=r;
 }
 public void run(){
  while(true)
   r.out(); 
 }
}
class Producer implements Runnable{

 public Resource r=null;
 Producer(Resource r){
  this.r=r;
 }
 public void run(){
  while(true)
   r.set("+商品+"); 
 }
}
public class Test5{
 /*
 要求:有多个生产者和多个消费者,生产者每生产一个商品消费者就消费一个商品
 分析:有多个生产者和多个消费都,所以可以用多线程来解决;
  多个线程用于生产,多个线程用于消费;因为商品是共享资源,所以我们得在对商品资源的操作上加上
  synchronized以保证资源的同步性;因为是生产一个消费一个,因而我们得用一个变量来保存资源的
  变化,可以用boolean变量来保存,当资源输入后boolean变为true;输出资源在判断到是true后就知道
  有资源可以输出,输出资源后将变量改为false;输入资源在判断为flase后继续输入,为true则线程等待
  在程序中我们得注意几个地方,一个是判断语句我们应该用while而不是if;因为while是循环判断而if
  只判断一次,用if语句,在只有一个生产者和一个消费者时程序不会混乱,但在有多个生产者和消费者
  时程序会发生错误;会出现生产一次消费多次或生产多次消费一次的情况;而在有多个生产者和消费者
  的情况下,程序中应该用notifyAll()而不是notify();用notify()有可能产生死锁情况
 */
 public static void main(String[] args){
  Resource r=new Resource();
  Consumer cr=new Consumer(r);
  Producer pr=new Producer(r);  
  new Thread(pr).start();
  new Thread(pr).start();
  new Thread(cr).start();
  new Thread(cr).start();
 }
}

 

 

原创粉丝点击