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

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

java基础知识-----多线程


引言

      理解线程,先要理解进程。

     1.进程

                一个正在执行的程序,每一个执行程序都有一个执行顺序又叫控制单元。

     2.线程

                 每一个控制单元就是线程。

     3.多线程

                   jvm启动时,有一个java.exe的进程,该进程中至少有一个线程执行。
                   而该线程执行的代码在main方法中--主线 程。  jvm中除了有主线程还有 一个负责垃圾回收的线程。
                  像这种在一个程序中有多个线程执行的方式称为多线程。    

     4.多线程的意义在哪? 

                 让线程产生同时运行的效果,提高运行效率。

     5.cpu运行原理

                1) 每一次运行效果不同源自其随机性。多个线程都在获取执行权,谁得到执行,谁就运行。 运行多久,cpu决定。
                2)为什么会有同时执行效果?在同一时刻,只能有一个程序执行,之所以会有同时执行的效果是因为cpu在多个进 程之间做着快速的切换。

一、线程的创建

创建有两种方式

1.  继承Thread方式

    1)定义类集成Thread  -------class Demo extends Thread
    2)复写run方法----------------public void run()
    3)调用start方法开启线程-----demo d = new demo(); d.start();
        注意只能用start方法,因为调用start会自动找run方法需要被执行的代码。进行线程开启。
        而d.run()只是进行了run方法的调用。,不是开启线程。


//继承的方式创建线程
class  Demo extends Thread
{
DemoName(String name)
{
super(name);
}
        //复写方法
public void run()
{
                //  此处放执行代码
for(int i=1;i<40;i++)
{
System.out.println(Thread.currentThread().getName()+"run"+i);
}
}
}
class Demo1
{
        public static void main(String[] args)
       {
            //创建线程
            Demo d1 = new Demo();
            Demo d2 = new Demo();
           //开启线程,调用start方法
          d1.start();
          d2.start();
       }
}

2.实现Runnable接口

    1)定义类实现Runnable接口-------class demo implements Runnable
    2)覆盖Runnable中的run方法-------public void run()
    3)创建线程对象---------------------Thread t = new Thread();
     3) 创建Runnable接口子类对象-----demo d = new demo();
       作为参数传递给Thread的构造函数,见上步。
    4)调用start方法  -------------------t.satrt();

为什么将Runnabl子类对象作为参数进行传递?
因为因定义的run方法所属的对象时Runnable子类对象,所以要让线程去指定对象的run方法,
就必须明确该run方法所属的对象。


//用实现的方式创建线程
class Demo implements Runnable
{
private int ticket = 20;
Object obj = new Object();
       
       //复写run方法
public void run()
{
synchronized(obj)//进行同步处理
{
while(true)
{	
if(ticket>0)
{
                                        //异常处理方式
try
{
Thread.sleep(10);
}
catch (Exception e)
{
                                                 System.out.println(e.toStrig());
}
System.out.println(Thread.currentThread().getName());
}
}
}
}
}
class Demo2
{
public static void main(String[] args) 
{
Demo d = new Demo();
                //创建线程
Thread d1 = new Thread(d);
Thread d2 = new Thread(d);
Thread d3 = new Thread(d);
                //线程启动,调用start()方法
d1.start();
d2.start();
d3.start();
}
}


实现与继承的区别
       实现:线程代码写在Runnab接口子类的run方法中。
       继承:线程代码写在Thread子类run方法中
实现的好处
      避免了单继承的局限性。建议使用。java只有单继承,没有多继承。


二、线程运行状态

                                            run结束,消亡
                                                    |stop
       被创建--------------------->运行<--(notify、notifyAll)------(wait、sleep)------>冻结
                                                    |
                                            临时阻塞:具备执行资格,但没有执行权


1.线程运行中出现的安全问题

             多条语句操作一个线程共享数据时,一个线程执行了一部分,还没执行完,另一个线程就参与执行,导致共享数据的错误。

2.对于安全问题java提供的解决方案

            ---同步代码块
            synchronized(对象)
            {  
                   同步代码 
             }

3.解决的原理

         保证同步中只有一个线程进行。加锁     
         好处                                                                                                   
                 解决了多线程的安全问题。
        弊端
                多个线程要判断锁,耗费资源
 -------------》加锁的前提:
                                           1)有2个或2个以上的线程
                                            2)多个线程要共用一个锁

4.扩展

        1) 同步的表现形式
            同步代码块
           Object obj = new Object();
          public void add(int n)
         {
              synchronized(obj)
             {
                 同步代码;
              }
           }

           2)同步函数
            public synchronized void add(int n)
            {
             }
           同步函数的锁是this,被对象调用,就有一个所属对象的引用
           当为静态同步函数时,锁是所属类对应的字节码文件  Class对象
          public static synchronized void add(int n)
          {
                synchronized(Ticjet.class)
               {
                      同步执行代码
               }
           }

          锁不是this,为什么?
          静态函数中不能定义this,静态static进入内存时,没有本类对象,但必有该类对应的字节码文件.class

5.死锁

          2个或者2个以上的进程在执行过程中,因为争夺资源而出现的一种相互等待的状态。
          若无外力作用,则无法进行下去,则称系统处于死锁状态,或者系统产生了死锁。
          当同步中嵌套同步时有可能出现死锁现象。
          要会写一个死锁程序
         
 
class Test  implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)
{
System.out.println("----if locka");
synchronized(MyLock.lockb)
{
System.out.println("---if lockb");
}
}
}
}
else
{
    while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("---else lockb");
synchronized(MyLock.locka)
{
System.out.println("---else locka");
}
}
}
}
}
class MyLock
{
static  Object  locka = new Object();
static Object lockb = new Object();
}
class  DeadLockTest
{
public static void main(String[] args) 
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}

6.利用锁的单利设计模式

   1)饿汉式
      
 class Single
        {
            private finale Single s = new Single();
           private Single(){}
            public static Single  getInstance()
           {
                   return s;
           }
         }



    2)懒汉式-------加锁
      
class Single
       {
               private Single s = null;
               private Single(){}
               public static synchronized Single getInstance()
              {
                      if(s==null)
                      {
                            synchronized(Single.class)
                             {
                                     if(s==null)
                                         s = new Single();
                             }
                      }
                   return s;
               }
         }



三、线程间通信

1.线程间通信

             多个线程操作同一个资源,但操作动作不同。
             当某些代码需要被同事执行时,用单独的线程进行封装。


/*
需求:买票程序
多个窗口同时买票
*/
class Ticket implements Runnable //实现Runnable接口好处:避免单继承的局限性
{
private  int tick = 100;
Object obj = new Object();//Object为所有类的子类,无需定义
public void run()
{                     //synchronized内放任意对象,
synchronized(obj)//同步代码块,{}内放需要被同步的代码 
{                //哪些被同步,就哪些操作共享数据
while(true)
   {
    if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e ){}//此行代码可以显示出不安全数据
System.out.println(Thread.currentThread().getName()+"sale :"+tick--);
    }
   }
}
}
}
class  maipiao2
{
public static void main(String[] args) 
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//实现Runnable语句  new Thread(对象p);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
                 t1.start();
t2.start();
t3.start();
t4.start();
}
}




/*
需求
银行有一个金库
有两个储户分别存300元,每次存100,存3次
目的:该程序是否有安全问题,如果有,如何解决?
如何找问题:
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
*/
class Bank
{
private int sum;
public void add(int n)
{
sum=sum+n;
System.out.println("sum="+sum);
}
}
class Cus implements Runnable
{
Bank b = new Bank();
public void run()
{
for(int i=0;i<3;i++)
{
b.add(100);
}
}
}
class    tongbuhanshu
{
public static void main(String[] args) 
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
}



2.等待唤醒机制

             均使用在同步中,因为要对持有锁的线程操作,只有同步有锁。
             操作方法定义在Object中
             原因:
                      操作方法都在同步中,要使用这些方法必须要标示所属的同步锁。
                     而且只可被同一锁唤醒,锁又可以是任意对象,任意对象调用的方法
                     一定在Object方法中。

3.sleep与wait的区别

           wait():等待,释放执行权,释放锁
           sleep():释放执行权,不释放锁。

4.为什么定义notifyAll()

           因为在唤醒对方线程时,如果使用notify,容易出现只唤醒本线程的情况,
          导致程序中所有线程都等待。

5.jdk1.5后,停止线程的方法

1) 停止线程的一般方法
        一般情况,有一个
         run()
          {
             while(flag)
            {
                 执行代码
            }
         }
       只要在main方法,该线程执行一段时间,将flag进行标记为false,则线程就结束
2)特殊情况
            线程处于冻结状态,不会读取到标记,就不会结束,
            当没有指定的方式让冻结的线程恢复到正常运行状态当中时,
            就需要对冻结进行强制解除,强制恢复到正常运行状态。
           此时就可以读取标记,结束线程。------Thread    -----interrupt

6.扩展

          1)join方法
              临时加入线程执行,当A执行到B的join时,A等待,B进行执行。
              当B执行完后,A再进行执行。
          2)守护线程
               又叫后台线程
               Thread.setDaemon(true);线程启动之前
              启动后与前台线程共同抢cpu执行权,当所有前台都结束后,后台自动结束。
          3)setPriority(Thread.MAX_PRIORITY);
                设置线程优先级。
                MAX_PRIORITY   最高级10级
                 MIN_PRIORITY    最低级 1级
                NORM_PRIORITY    默认优先级5级
              


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



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值