黑马程序员--JAVA多线程


----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1,线程与进程

进程:

进程就是程序的一次执行活动,这个程序在内存运行时进行内存分配空间,它是一个控制单元,运行了一个程序就是启动了一个进程

线程:

线程是一个程序内部的一条执行路径或者一个控制流,一个程序里可以有多个线程流也就是说JAVA语言允许程序多线程并发运行.

进程与线程的区别:

功能上:线程是处理器的调度对象,而进程是资源分配的对象。
内存上:每个进程都有独立的代码和数据空间并且进程间的切换开销大,同一个进程内的多个线程共享相同的代码和数据空间可以实现线程间的通信和同步操作,每个线程有毒瘤的运行栈和程序计数器,线程的切换开销小。
扩展:JAVA虚拟机JVM不只一个线程,还有垃圾回收机制的线程,不过这是守护线程。

线程内部的运行原理

线程是由CPU来切换处理的,也就是说CPU可以随意乱序的处理线程,哪个线程获得执行权CPU就执行哪个线程
线程的名字如果不指定,那默认的就是Thread_编号定义,编号是从0开始的,获得程序中当前执行线程名字方法为:Thread.currentThread().getName()
线程在主函数中调用Star方法来启动线程,启动线程后程序会直接去找该线程的run方法,这样内存中就多了一条线程流,我们开发之用就是来重写run()方法执行我们的程序

2,如何自定义线程呢?

方法一:继承Thread方法,子类重写Run()方法

1,定义类继承Thread类
2,重写父类中Run方法
3,通过创建子类对象,创建线程对象
4,调用线程的Star方法,开启线程,接着调用重写后的Run方法
class Demo extends Thread//定义类继承Thread类  
{  
    public void run()//重写Run方法  
    {  
        for(int x=0; x<60; x++)  
            System.out.println("demo run----"+x);  
    }  
}  
class ThreadDemo   
{  
    public static void main(String[] args)   
    {  
        //for(int x=0; x<4000; x++)  
        //System.out.println("Hello World!");  
        Demo d = new Demo();//创建自定义的线程对象。  
        d.start();//对象调用Star方法,开启线程。  
        //d.run();//线程开启后会调用run方法开始执行重写后的run方法  
        for(int x=0; x<60; x++)  
            System.out.println("Hello World!--"+x);  
    }  
}

方法二:实现Runable接口

1,自定义子类实现Runable接口
2,实现Runable中的run()方法
3,通过new一个thread类传入子类对象来创立线程对象
4,调用thread类的Star方法开启线程并调用重写的Run方法
通过一个多窗口卖票程序来说明一下:
class Ticket implements Runnable//创建自定义子类实现Runable接口  
{  
    private  int tick = 100;  
    public void run()  //实现Runable方法的run()方法
    {  
        while(true)  
        {  
            if(tick>0)  
            {  
                System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  //输出当前执行的线程
            }  
        }  
    }  
}  
class  TicketDemo  
{  
    public static void main(String[] args)   
    {  
        Ticket t = new Ticket();  //创建自定义子类的对象
        Thread t1 = new Thread(t);//通过new一个thread类传入子类对象来创立线程对象
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t); 
        Thread t4 = new Thread(t); 
        t1.start();//调用thread类的Star方法开启线程并调用重写的Run方法
        t2.start();  
        t3.start();  
        t4.start();  
        /* 
        Ticket t1 = new Ticket(); 
        //Ticket t2 = new Ticket(); 
        //Ticket t3 = new Ticket(); 
        //Ticket t4 = new Ticket(); 
        t1.start(); 
        t1.start(); 
        t1.start(); 
        t1.start(); 
        */  
    }  
}  //不加锁是会打印出0,-1,-2的错票,这是因为pc多核处理器的缘故
如果不想让结果输出的特别快,可以让线程休眠一定时间
Try {  
  Thread.sleep(10);
}catch(InterruptedException e){}
第二种通过实现Runable接口来创立线程是因为第一种继承Thread类具有局限性,因为Java是单继承,不能继承多个功能
实现方式与继承方式的区别在于实现的run方法在不同的自定义子类中

3,线程的生命周期



注意:Star()方法只执行一次,sleep()方法需要指定睡眠时间,单位是毫秒

线程安全问题

多线程不安全原因:

多线程的操作会使线程对同一资源因为不同步造成数据改动的错乱,为了解决这个问题,我们只允许在同一个资源只能由一个线程执行并且其结果与其他线程同步

安全问题专业解决方案:

运用同步代码块
synchronized(实例对象) //这个对象就是锁。
{
    需要被同步的代码
}
对象如同锁,持有锁的线程才能进行资源改动

同步

同步的条件:
1,必须要有两个或者两个以上的线程
2,必须是多线程使用一个锁
优点和缺点:可以解决多线程的安全问题,但多线程需要判断锁,耗资源,产生死锁问题
同步函数被static修饰时,这时的同步用的锁就是该类的 字节码文件对象类名.class
同步代码块和同步函数的区别在于同步代码块使用的锁可以是任意对象,同步函数使用的锁是this(静态同步函数的锁是该类的字节码文件对象)
一个类中只有一个同步就可以使用同步函数,但如果有多个同步,必须使用同步代码块

多线程之单例模式

延迟加载的单例模式最好是写懒汉式,加上同步可以解决多线程访问,但效率不高,所以我们可以用双重判断形式解决
class Single  //懒汉模式
{  
  private static  Single s=null;  
  private Single(){}  
  public static  Single getInstance()  
  {  
      if(s==null)  
      {  
         synchronized(Single.class)  //同步
          {  
             if(s==null)  
//        --->A  
//        --->B  
             s=new Single();  
           }  
      }  
      return s;  
  }  
}  
线程的通信唤醒机制
多线程操作同一个资源,但操作的动作不一样,我们只需要执行一个线程
wait方法:同步的线程处于冻结状态,释放了执行权,释放了资格
notify:唤醒线程中的一个等待线程
notifyAll:唤醒的是线程池中的所有线程
wait和sleep区别:
wait时间不指定,只能由notify或者notifyAll唤醒,会释放执行权,释放锁
sleep必须指定时间,时间到了就可以从冻结状态转为运行状态,释放执行权,不是放锁
-----------------
生产者与消费者情况
public class ProducerConsumerDemo {  
    /**多生产者消费者的情况 
     * 当出现多个线程时,要用while标识和notifyAll 
     * @param args 
     */  
    public static void main(String[] args) {  
        Resource r=new Resource();  
        Produce pro=new Produce(r);  
        Consumer con=new Consumer(r);  
    Thread t1=new Thread(pro);  
    Thread t2=new Thread(pro);  
    Thread t3=new Thread(con);  
    Thread t4=new Thread(con);  
    t1.start();  
    t2.start();  
    t3.start();  
    t4.start();  
    }  
}  
class Resource  
{  
 private String name;  
 private int count=1;  
 private boolean flag=false;  
 public synchronized void set(String name)  
 {   while(flag)//用了while出现了全部等待  
    try {  
        wait();  
    } catch (InterruptedException e) {  
        // TODO Auto-generated catch block  
        e.printStackTrace();  
    }  
     this.name=name+"------"+count++;  
     System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);  
  flag=true;  
  this.notifyAll();  
 }  
 public synchronized void out()  
 {  while(!flag)  
     try {  
            wait();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
     System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);  
   flag=false;  
   this.notifyAll();  
 }  
}  
class Produce implements Runnable  
{  
 private Resource res;  
 Produce(Resource res)  
 {  
     this.res=res;  
 }  
 public void run()  
 {  
     while(true)  
     {  
         res.set("商品++");  
           
     }  
 }  
}  
class Consumer implements Runnable  
{  
     private Resource res;  
     Consumer(Resource res)  
     {  
         this.res=res;  
     }  
     public void run()  
     {  
         while(true)  
         {  
             res.out();  
         }  
}  
}  

JDK1.5新特性标准

JdK1.5提供了多线程升级解决方案,将同步synchronized替换成现实LOCK操作
将wait,notify,notifyAll替换了condition对象
线程停止的Stop方法已经过时,新的停止方法有两种
1,定义循环结束标记
2,使用interrupt(中断)方法,该方法是结束线程的冻结状态使线程回到运行状态中
运用LOCK操作代替同步,之前的wait,notify,notifyall方式用lock中的await,signal(),signAll()
class BoundedBuffer {
   final Lock lock = new ReentrantLock();//生成lock实例对象
   final Condition notFull  = lock.newCondition();
   final Condition notEmpty = lock.newCondition();
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
   public void put(Object x) throws InterruptedException {
     lock.lock();//加锁
     try {
       while (count == items.length)
         notFull.await();//等待
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();//唤醒
     }
    finally {
       lock.unlock();//解锁
     }
   }
   public Object take() throws InterruptedException {
     lock.lock();//加锁
     try {
       while (count == 0)
         notEmpty.await();//等待
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();//唤醒
       return x;
     }
finally {
       lock.unlock();//解锁
     }
   }
 }

----------------------- android培训java培训、java学习型技术博客、期待与您交流! ----------------------

详情请查看:http://edu.csdn.net/heima

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值