黑马程序员-------java基础之线程

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

一 线程是啥?

     每一个运行着得程序对应一个进程,是cup分配资源的最小单元。

     而每一个进程都包含一个和多个线程,程序(进程)所完成的功能,实际是由一个个独立的线程进行完成的。

    比如:

   JVM启动的时候会有一个进程:java.exe该进程中至少有一个线程在负责java程序的执行,而且这个
线程运行的代码在于main方法中。该线程成为主线程。

二java中创建线程的方式

   方式1:继承实现方式

    1  定义一个Thread类的子类

    2  复写run() 方法

         目的:定义该线程所需要执行的代码。即定义该线程的功能

    3 创建该类对象

   4  该对象.start()

       作用:让创建的线程处于可抢夺CUP的状态(临时状态或就绪状态)

  方式2:实现方式

   1  定义一个类实现Runnable接口

   2  复写run()方法

   3 用该类对象作为参数创建一个Thread对象:  Thread  t = new Thread(实现Runnable接口类的对象)

  4 t.start();//让创建的线程处于可争夺CPU的状态

为啥2种方式都要复写run方法?

因为JAVA中定义Thread类来描述线程

 1   该类定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
   也就是说:thread类中的run方法,用于存储线程要运行的代码。

 2  Thread类也实现了Runnable接口

实现方式和继承方式的区别?

实现方式的好处:避免了单继承的局限性,
                                在定义线程时,建议使用实现方式。

两种方式的区别:
   继承Thread类:线程代码存放在Tread子类的run()方法中。
   实现Runnabel接口:线程代码在接口子类的run()方法中。


线程的几种状态:



三 线程安全问题

   为啥会出现安全问题?

    原因:不同线程对线程共享数据进行访问的,多条代码可能只执行一部分之后就停止(CPU的切换)

  java 为线程线程安全问题所提供的专业解决办法:

 同步代码块:

  synchronized(对象)

    {

          操作共享数据的代码

     }

  对象相当于锁,持有锁的线程可以再同步代码块中执行,没有锁的线程即使获得了cpu
  的执行权,也进不去。保证了对共享数据操作的安全性。

 保证多个线程同步的前提:

   1   必须是2个或2个以的线程

   2    必须保证是同一个锁。

           目的: 保证了同步代码块儿中的代码只有一个线程在运行。
 

同步的缺点:多个线程需要进行锁的判断,较为消耗资源。

如何确定同步代码块儿所加的位置:

1 明确哪些代码是多线程运行的代码。-----Run()中的所有

2 明确共享数据。

3 明确多线程运行代码中的哪些语句是操作共享数据的。//同步代码块的使用地方

-------------------------------------------------------------

同步的第二种实现方式:同步函数

例如:

      public synchronized void  add(int n)
{

}

同步函数用的是哪一个锁?
函数需要被对象调用,那么函数都有一个所属的引用对象。就使用的锁是this。
所以函数是this

静态函数的同步锁是该方法所在类的字节码文件对象类名.Class;

因为静态函数不存在this引用


死锁:

死锁发生的原因:1 存在多个锁

                                 2 同步中嵌套者同步

示例:

class Test implements Runnable
{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
			synchronized(Lock.locka)
			{
			    System.out.println("Test locka");
				synchronized(Lock.lockb)
				{
					System.out.println("Test lockb");
				}
				
			}
	}
	
}

class Test2 implements Runnable
{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(Lock.lockb)
		{
			System.out.println("Lockb  test2");
			synchronized(Lock.locka)
			{
				<pre name="code" class="java">                         System.out.println("locka test2");
}}}}class Lock{static Object locka = new Object();static Object lockb = new Object();}public class deadTreadDemo {public static void main(String[] args) {// TODO Auto-generated method stubTest t = new Test();Test2 t2 = new Test2();Thread th1 = new Thread(t);Thread th2 = new Thread(t2);th1.start();th2.start();}}

 当 th1 线程获得cpu后执行到 
System.out.println("Test locka");失去了CPU的执行权, 但并未释放同步锁对象locka
此时th2 线程获得cpu的执行全当执行到:

System.out.println("Lockb  test2");失去了cpu的执行权,但并未释放同步锁对象lockb
此时th1再次获得cpu执行权,但并未获得lockb对象因而无法进入下一个同步块,无法执行
System.out.println("Test lockb");而无法结束线程释放同步锁locka
当一段时间后Th2线程在获得cpu执行权,但并未获得同步锁对象locka因为无法执行下一个同步块

无法执行

<pre name="code" class="java"> System.out.println("locka test2");

 无法结束线程释放同步锁lockb 

这样就导致2个线程再循环等待资源,死锁。

------------------------------------------------------------------------------------------

线程间的通信:

wait();

notify();

notifyAll();

以上方法都定义在Object类中;因为锁是任意对象,而线程的wait(),notify();notifAll()只是针对某个具体的锁而言的。实质是: 锁对象.wait();锁对象.notify();


通信:一个线程唤醒另一个线程

问题描述:当存在多个线程对某一共享资源进行操作时,线程之间的操作应当满足一定的顺序时(如:先输入才能输出)如何实现?

此时就要用到线程之间的通信机制。

实例:

生产者消费者问题

<pre name="code" class="java">//
 class Resource
  {
     private boolean flag = false;//信号量:标志是否有商品
     private String name;
     private int num =0; 
     public void put(String name)throws InterruptedException//name 和num是共享数据
       {
         synchronized(this)//只是保证同步了 但还并未保证先生产后消费:不能保证生产一个消费一个交替运行
         {
          if(flag)//如果有商品 则生产停止
           {
              wait();//放弃CPU的执行权等待消费者的唤醒
            }
             this.name = name;
             num++;
             System.out.println(name+"----生产者-----"+num);
             flage = true;//有货了
             notify();//打电话给消费者说有货了
         }
       }
     public void get()throws InterruptedException
      {
         synchronized(this)
        {
           if(!flag)//没有消费品
             {
                wait();//消费者等待
             }
             System.out.println(name+"----消费者-------"+num);
             flag = false;//没货了
             notify();//通知生产者没货了(唤醒生产线程)
        }
      }
    
  }

 class Producer implements Runnable
{
  private Resource r;
  public Producer(Resource r)
   {
     
       this.r = r;
   }

   public void run()
     {
       while(true)
        {
          try
          { 
           r.put("商品");
           }
           catch(InterruptedException e)
           {
           }
        }
      }

} 

class Consumer implements Runnable
{
  <pre name="code" class="java"><pre name="code" class="java"> private Resource r;
  public Consumer (Resource r)
   {
       this.r = r;
   }

   public void run()
     {
       while(true)
        { 
          try
           { r.get();
           }
           catch(InterruptedException e )
            {
            }
        }
      }

 

}

class ProConDemo1
{
public static void main(String[]arg)
{
Resource r = new Resource();
Producer pro = new Producer (r);
Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
 
 新问题的产生: 

 当存在多个消费者线程和多个生产者线程时:

 可能会出现:生产多个商品但值被消费一个;或者是生产一个商品被多个消费者消费的情况

 产生原因:

       1   当线程在if(){}块中wait()之后,当再次获得CPU执行权时不会再对flag进行判断;从而出现多次消费,或者多次生产的现象;

             解决办法:将if(flag)改为:while(flag);  让线程始终对flag进行判断

     2   改成while()之后出现新的问题:

        所有线程都wait()了。让程序无法正常执行。

        原因:notify();唤醒的是:先进入等待所队列中的线程,这样有可能唤醒的是己方(同为生产者线程或者同为消费者线程)线程;

        解决办法:notifyAll();

具体分析:

<pre name="code" class="java"><img src="https://img-blog.csdn.net/20140822103745437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE5MDQ5OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" height="710" width="907" alt="" />

 


JDK1.5中提供新方法解决锁问题:

用实现Lock接口的类 代替synchronized,使用condtion 来进行wait(),notify() 等操作

lock的特点:可以为同一个lock设立多个condition对象,为不同线程分类:如将线程分为生产者线程类,消费者线程类。当线程间进行唤醒操作时可以对同一个lock的不同类线程进行唤醒。

实例:

<pre name="code" class="java"> import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/*JDK 1.5以及之后对锁的处理
 *  1 用 Lock接口的实现类对象 替代 synchronized
 *  2 用 condition 来进行wait(),notify(),notifyAll()操作
 *  3 特点:
 *     1 需显示的加锁  Lock  lock.lock();
 *     2 显示释放锁    lock.unlock();
 *     3 一个锁可对应多个condition对象
 * */
class Product1//拉煤优化进程间通信 优化代码
{
    private String name;
    private int num = 0;
    private boolean flag = false;
    private ReentrantLock lock  = new ReentrantLock();
    private Condition producer_con;
    private Condition consumer_con ;
    public Product1(String name)
    {
        this.name = name;
        producer_con = lock.newCondition();
        consumer_con = lock.newCondition();
    }
    
    
    //生产行为
    public void put()throws InterruptedException
    {
        
               lock.lock();//显示调用加锁
               try
               {
                    while(this.flag)
                    {
                       producer_con.await();//可能出现异常,出现异常时必须释放锁       
                    }
                      System.out.println(name+"---"+"生产者----"+(++num));//模拟生产者进行的操作处理
                      flag = true;
                    //this.notifyAll();//唤醒消费者
                    consumer_con.signal();
               }finally
               {
                lock.unlock();//显示释放锁  
               }
    }
    //消费行为
    public  void get() throws InterruptedException
    {
        
              lock.lock();
              try
              {
                    while(flag == false)
                    {
                      consumer_con.await();                
                    }
                    System.out.println(name+"---"+"消费者--------------"+num);
                    flag = false;
                    //this.notifyAll();//唤醒生产者
                    producer_con.signal();// 只唤醒 生产者线程不唤醒本方线程
              }
              finally
              {
                    lock.unlock();    
              }
    
}
}
//生产者
class Producer1 implements Runnable
{
    private Product1 product;
    public Producer1(Product1 product)
    {
        this.product = product;
    }
    public void run()
    {
        
      while(true)
      {
            try{
                product.put();
                }
            catch(InterruptedException e)
            {
                        
            }
                
      }
    }
    
}
//消费者
class Consumer1 implements Runnable
{
    private Product1 product;
    public Consumer1(Product1 product)
    {
        this.product = product;
    }
    public void run()
    { 
       while(true)
       {
            try
            {
              product.get();
            }
            catch(InterruptedException e)
            {
                
            }
       }
    }
}
public class producerConsumerDmeo2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Product1 product = new Product1("商品");
        
        Producer1 producer = new Producer1(product);
        Consumer1 consumer = new Consumer1(product);
        
        Thread t1 = new Thread(producer);
        Thread t2 = new Thread(producer);
        Thread t3 = new Thread(consumer);
        Thread t4 = new Thread(consumer);
        
        t1.start();//问题的产生:当存在多个生产者,多个消费者时:一个商品被2个消费者消费
        t2.start();
        t3.start();
        t4.start();   
    }

 

四 线程中的常用函数

线程停止的方法

stop():已过时

interrupt();

如何停止一个线程:

  从本质上说:只有一个方法:结束Run方法。

当开启一个线程时:线程运行的通常是循环结构的代码,所以要结束run();只要控制住循环。

特殊情况:

  当线程处于冻结状态。
  就不会读取到标记。那么线程就不会结束

当没有指定的方式来让冻结的线程回复运行状态时,这时需要对冻结进行清除。
强制让线程回复到运行状态中来,这样就可以操作标记让线程结束。

interrupt:清除线程的冻结状态
调用一个线程的interrupt()方法时会触发InterruptedException 异常

在异常处理函数中修改循环条件。

setDaemo();

t1.setDaemon();让t1成为守护线程(后台线程);当前台线程(创建t1的线程)结束时t1会自动结束

注意:该方法必须在线程开启之前调用

join()方法

当A线程,执行到了B线程的.join()方法时,A就会等待B线程执行完
A才会执行。
join可以用来临时加入线程。


线程优先级
t.setPriority(int )//优先级 只有10级
1:MIN_PRRORITY
5:NORMAL_PROORITY
10:MAX_PRORITY    

yield() 方法;
Thread.yield();//线程会释放执行权。类似sleep();



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值