黑马程序——多线程完整版总结(黑马我来了之十)

熬夜扛不住了

先睡会。

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


1、进程:进程就是一个正在执行的程序。

2、线程:线程是进程执行的一条线索或路径。进程中至少有一个线程存在。

3、多线程:顾名思义,一个进程中的多个线程。

        线程的开始:当要运行一个程序时,JVM首先会找到main函数,然后从main函数开始执行(也就是说,程序是从main函数开始运行的)

    此时,程序就成为一个进程,既然是进程肯定有线程的存在。此时的线程就是主线程,主线程会向下顺序执行代码。

        如果程序中存在一个庞大的循环语句,主程序就会一直在这里运行,直到循环语句结束,下面的代码才能被执行到。

    这可能要花费较长的时间,影响了程序运行的效率。所以,为了提高程序的效率,就引入了多线程。由主线程开辟另一个或多个线程,

    让这些线程都去执行代码。

    

    创建线程的两种方法:

        一、1.定义一个类继承Thread,重写Thread中的run方法,run方法中存放自定义的要让该程序执行的代码。

            2.用子类new一个线程对象,线程对象调用线程中的start方法,来开启线程。

                class Demo extends Thread

                {

                    public void run()

                    {

                        System.out.println("我要运行的代码");

                    }

                }

                class ThreadDemo

                {

                    public static void main(String[] args)

                    {

                        Demo d=new Demo();

                        d.start();

                    }

                }

        二、1.实现Runnable接口,复写run方法。

            2.Threadnew一个线程对象,并将Runnable的子类对象传入Thread的构造方法中。

                class Demo implements Runnable

                {

                    public void run()

                    {

                        System.out.println("Hello World");

                    }

                }

                class ThreadDemo

                {

                    public static void main(String[] args)

                    {

                        Thread t=new Thread(new Demo());

                        t.start();

                两种方法的比较:Java中的类只能继承一个类,第一种方法中,当Demo继承了Thread之后,就不能再继承其

                他类,但Demo很有可能是另一个类的子类,这时就出现了局限。第二种方法中,Demo实现了接口Runnable

                同时还能成为其他类的子类,使用较多。

        

        单核状态下,在某一时刻只有一个线程在运行。某一时刻可能有多个进程,每个进程中也可能有多个线程,CPU就在这

    些线程间来回的切换,线程也只有在获得CPU的执行权时才能运行。

4、线程的四个运行状态:

            1.运行:既有执行资格又有执行权。

            2.堵塞(临时状态):有执行资格没有执行权。等待CPU的执行权,一旦获得就进入运行状态。

            3.冻结:没有执行资格也没有执行权。CPU不会分配执行权给它,直到它被唤醒。

                    从运行状态进入冻结状态:sleep(time)wait();

                    被唤醒:sleep的时间到或notify().

            4.消亡:run方法结束,或stop()

            

5、多线程的安全问题:

什么时候会出现线程安全问题?

 满足3个条件时,会出现:

 1.存在多个线程 

 2.多个线程共享数据(共享数据一般是成员变量,局部变量不是)。

 3.操作共享数据的语句至少要有两条。

6、多线程安全问题的解决:同步。因为同步可以保证多线程代码只能被持有锁的线程运行,其它线程不能运行。

 任意时刻,一个锁只能被一个线程拥有。

            (持有锁的线程会在执行完多线程代码时释放锁,这样锁就能被其它线程拥有。)

          1.同步代码块;将要同步的代码放synchronized中,并加锁。

              synchronized(对象)//只要是个对象就行,这个对象就是锁

              {

                  要被同步的代码;

              }

          2.同步函数:synchronized能将代码封装并同步,函数只能将代码封装,所以用synchronized修饰函数,让函数既能封装又能同步。

            同步函数也有锁。非静态同步函数的锁是this,同步函数所属对象的引用;静态同步函数的锁是:

类名.class,函数,     所属类的字节码文件对象。

                  需注意的是:1.不需要同步的代码不要放入同步函数中。2.synchronized放在函数返回值类型前

                  public synchronized void show()

                  {

                      要被同步的代码;

                  }

          找到 要被同步的代码 的方法: 

1.先找到 多线程所有要运行的代码(可能是一个run方法被多个线程使用同,也可能是一个run方法只被其中的一个线程使用); 

2.多线程的共享数据(一般成员变量都是)

3.多线程要运行的代码中操作 共享数据 的代码就是 要被同步的代码。              

     注意:有时使用了同步仍然不成功,出现这种现象的原因可能是:需要被同步的代码可能放在程序的不同位置,有的代码要用到同步代码块,有的要用同步函数

    那么怎么才能使同步成功?

                  1.首先判断要被同步的代码是否正确,确定所有要被同步的代码都被同步了。

                  2.同步代码块和同步函数中的锁是同一个锁,即必须是 多个线程使用同一个锁。。               

                  

7、同步时要避免的问题:死锁。

同步嵌套同步时会发生死锁。

            if(flag)

            {

                while(true)

                {

                    synchronized(Lock1)

                    {

                        System.out.println(".......");

                        synchronized(Lock2)

                        {

                            System.out.println("???????");

                        }

                    }

                }

            }

            else

            {

                while(true)

                {

                    synchronized(Lock2)

                    {

                        System.out.println(".......");

                        synchronized(Lock1)

                        {

                            System.out.println("???????");

                        }

                    }

                }

            }

      当线程1满足if条件,只执行到 System.out.println("......."); 间片到了,线程2获取执行权,并满足else条件时,执行到一半,时间片也到了,

      此时,线程一持有锁Lock1,线程二持有锁Lock2,都在等待对方执行完后释放锁,但是都无法执行完,这时就会形成死锁。

8、线程间通信;多个线程共享一段数据,但是对数据的操作不同。

等待唤醒机制:wait();notify();

notifyAll();三个方法都是Object类中的方法,都用来操作持有锁的线程,所以都只能用于同步。

notify()方法,唤醒内存中等待线程池中的第一个在等待的线程。

方法的使用:1.这三个方法在使用时,都必须标明它们将要操作的线程持有的锁,

 即lock.wait()lock.notify()lock.notifyAll()

2.等待和唤醒只能是同一个锁。即,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。

 不可以对不同锁中的线程进行唤醒。

为什么三个方法都在Object中定义?

因为使用这三个方法在使用时,都通过锁来调用,而锁可以是任意对象,所以要定义到Object中。

一个生产者一个消费者:if 

notify

多个生产者多个消费者:whilenotifyAll

升级的多个生产者多个消费者,JDK1.5新特性:1.synchronizedLock替代--->lock.lock()获取锁lock.unlock()释放锁

 2.waitnotifynotifyAll三个方法被Condition代替--->condition.await()等待,condition.signal()唤醒一个线程,condition.signalAll()唤醒所有线程。

  通过不同的Condition实例condition_con.signal() condition_pro.signal(),可以实现 只唤醒消费者线程,旧方法只能唤醒所有线程。

  获取Condition实例,用Lock中的方法,lock.newCondition();

class Resource

{

private String name;

private int count = 1;

private boolean flag = false;

//  t1    t2

private Lock lock = new ReentrantLock();

private Condition condition_pro = lock.newCondition();

private Condition condition_con = lock.newCondition();

public  void set(String name)throws InterruptedException

{

lock.lock();

try

{

while(flag)

condition_pro.await();

this.name = name+"--"+count++;

System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);

flag = true;

condition_con.signal();

}

finally

{

lock.unlock();//释放锁的动作一定要执行。

}

public  void out()throws InterruptedException

{

lock.lock();

try

{

while(!flag)

condition_con.await();

System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);

flag = false;

condition_pro.signal();

}

finally

{

lock.unlock();

}

}

}

class Producer implements Runnable

{

private Resource res;

Producer(Resource res)

{

this.res = res;

}

public void run()

{

while(true)

{

try

{

res.set("+商品+");

}

catch (InterruptedException e)

{

}

}

}

}

class Consumer implements Runnable

{

private Resource res;

Consumer(Resource res)

{

this.res = res;

}

public void run()

{

while(true)

{

try

{

res.out();

}

catch (InterruptedException e)

{

}

}

}

}

停止线程:有两种方法--->run方法结束、stop()

 stop方法已经过期,所以只能使run方法结束。

 分析:程序中之所以使用多线程,通常是因为出现了较大的循环结构,即,多线程运行的代码通常是循环结构。

  所以,只要控制住循环,就可以使run方法结束,也就结束了线程。使用标记。

  特殊情况:当线程处于冻结状态(wait()了),就不会读取到标记,那么线程就无法结束。

   这时,可以使用Thread中的interrupted()中断线程,中断线程是将线程从冻结状态强制恢复到运行状态,清除掉了冻结状态,这样就能继续读取标记。

9Thread类中的几个方法

1.setDaemon():可以将线程标记为守护线程,该方法必须在启动线程前调用---->thread.setDaemon(); thread.start();

  守护线程可以理解为后台线程,后台线程在开启之后会和前台线程共同抢占cup执行权,当所有前台线程都结束时,后台线程会自动结束。这样也可以实现停止线程。

2.join():等待该线程终止.A线程执行了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。-->b.join();

 若B线程执行到中间wait了,那么A就会一直等,这时可以使用b.interupt()强行使B线程切换到运行状态。

3.setPriority():设置线程的优先级。线程的优先级表示抢占CPU的频率,值越大优先级越高。共有10个等级,从110,所有线程默认的优先级是5

   Thread类中,1,5,10分别用静态常量表示:MIN_PRIORITYMORM_PRIORITYMAX_PRIORITY

   使用--->thread.setPriority(MAX_PRIORITY);thread.setPriority(3);


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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值