Java基础之多线程(持续更新中)

多线程
     进程:正在运行的应用程序(直译)
     线程:就是进程的执行单元,一条执行路径
     多线程:多线程就是一条应用程序有多条执行路径

                  一个进程中至少要有一个线程

                  开启多个线程是为了同时运行多部分的代码

                  每一个线程都有自己的运行内容,这个内容可以称之为线程要执行的任务

比如说:迅雷下载,360杀毒,生活中也有多线程,刷牙洗脸烧水例子

 

多线程并不是同时执行的

     比如,现在开启的程序有两个,这两个程序并没有同时执行,cpu只是在做着特别特别特别快的切换,在同一时刻,只能运行一个线程。切换的频率是非常快的,随机的(由时间片决定,也就是cpu分配给每个程序的时间)。如果程序开的特别多,那么被切换到的概率和频率就小了,所以卡机。想要不卡机,弄两个cpu,到后来才叫双核,四核的。

没有学多线程前,我们做的是多线程程序吗?JVM,GC演示

     按理来说,我们并没有看到多个线程在同步执行,但是通过分析,至少有两个线程

     1.因为main函数执行的时候,需要一个线程,该线程的任务代码就是main函数里内容

     2.一个匿名对象,这个匿名对象new完了就垃圾了,垃圾回收器回收又是一个线程

     3.不是说主线程执行完毕后虚拟机就执行完了,虚拟机要等待其他线程执行完毕

public class Demo

{

     public static void main(String[] args) throws Exception

     {

            new Demo2();

            new Demo2();

            new Demo2();

            //如果不手动调用回收垃圾函数,GC机制会在垃圾多的时候在回收

            System. gc();

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

     }

}

class Demo2 extends Object

{

     public void finalize()

     {

            System.out.println( "我被回收啦!" );

     }

}

 

for(int x = -999999; x < 999999; x++){}控制代码的延迟


为什么使用线程?什么时候使用?
     为了提高效率才使用,只有要操作代码的内容比较复杂,比较耗时,多的时候使用
    
如何实现多线程程序?
     线程依赖于进程的存在。进程是由操作系统创建的。java不能直接调用操作系统的功能进程是无法通过系统来获取的。不像是c,c++,但是java可以调用底层c写好的类,线程类Thread。因为main函数就是一个线程,我们想是Java.lang包下的!
通过查看API,我们知道创建线程的方式有两种
     方式1 :

          继承Thread类
          并且重写Thread中的run()方法

为什么要这样?

  创建线程的目的是为了开启一条运行路径,去运行指定的代码和其他线程代码同时运行

  JVM创建的主线程的任务都定义在了主函数中

 Thread类用于描述线程,线程是需要任务的,所以Thread类也对任务进行了描述,这个描述就是通过Thread类中的run函数体现的。也就是说,run函数就是封装了运行任务的函数。

          直接创建Thread的子类对象创建线程

          调用start函数开启线程并调用线程的任务run()函数执行

public class Demo

{

     public static void main(String[] args) throws Exception

     {

           Demo2 d1 = new Demo2( "旺财");

           Demo2 d2 = new Demo2( "小强");

           d1.start();

           d2.start();

     }

}

class Demo2 extends Thread

{

     private String threadName;

     public Demo2(String threadName)

     {

            this. threadName = threadName;

     }

     public void run()

     {

            for( int x = 1;  x <= 10; x++)

           {

                System. out.println( threadName + x);

           }

     }

}

线程的一些函数

     Thread getName()获取线程的名称 Thread-编号(从0开始)

    Thread.currentThread().getName()获取的是当前线程的名字

 

     Demo d = new Demo("旺财");

    public Demo(String name)

    {

       super(name)

    }

    调用父类的一个参数构造函数给线程名字

 

线程运行问题

主线程和其他线程互不干扰,各自调用各自的函数,出现异常只结束当前线程,不会结束JVM

 

 

线程的5种状态

     

          

方式2:

实现Runnable接口

覆写接口中的run函数,将线程的任务代码都封装到run函数中

通过Thread类创建线程对象,并将Runnable接口的子类作为Thread类的构造函数的参数进行传递

调用线程对象的start函数,开启线程

class MyRunnable implements Runnable

{

 

     public void run()

     {

          for ( int x = 0; x < 100; x++)

          {

      System.out.println(Thread.currentThread().getName() + "---hello"+ x);

          }

     }

 

}

public class MyRunnableDemo

{

     public static void main(String[] args)

{

          MyRunnable my = new MyRunnable();

          Thread t1 = new Thread(my);

          Thread t2 = new Thread(my);

 

          t1.setName( "乔峰");

          t2.setName( "慕容复");

 

          t1.start();

          t2.start();

     }

}

 

为啥有第一种了还要用第二种方案
  避免了第一种单继承的局限性
  将线程中任务从线程子类中分离出来,进行了单独的封装。避免了只为了用父类的某个功能而继承

  

卖票案例和IllegalStateException

class MyThreadImpl implements Runnable

{

     private int ticket = 100;//不用static,不局限性,因为每个线程都是作为Runnable接口的子类进行传递

     @Override

     public void run()

     {

            while( true)

           {

                 if( ticket > 0)

                {

                      //因为父接口里面没有抛出异常,所以子类只能try处理

                      try{Thread. sleep(10);}catch (InterruptedException e) {}

                     

                      //这里出现了线程安全问题,可能同时进来了,没有一个一个if判断

                      //那么就会出现-1 -2等情况

                     System. out.println(Thread. currentThread().getName() + "正在出售第" + ticket-- + "张票!");

                }

           }

     }    

}

public class Demo

{

     public static void main(String[] args)

     {

           MyThreadImpl mti = new MyThreadImpl();

           Thread t1 = new Thread(mti);

           Thread t2 = new Thread(mti);

           Thread t3 = new Thread(mti);

           t1.setName( "线程一");

           t2.setName( "线程二");

           t3.setName( "线程三");

 

           t1.start();

           t1.start();

               Exception in thread "main"异常所在线程 java.lang.IllegalThreadStateException异常类型

               at java.lang.Thread.start( Thread.java:595 )

               at Demo.main( Demo.java:23 )异常发生的位置

           t2.start();

           t3.start();

     }

}

 

卖票发生出现负数的票原因

     1.在多线程的情况下

        2.有共享数据被多个线程所操作

        3.操作共享数据的代码有多条

 

同步代码块解决问题

class MyThreadImpl implements Runnable

{

     Object obj = new Object();

     private int ticket = 100;//不用static,不局限性,因为每个线程都是作为Runnable接口的子类进行传递

     @Override

     public void run()

     {

            while( true)

           {

                 synchronized( obj)//obj标识,任意对象

                {

                      if( ticket > 0)

                     {

      System. out.println(Thread. currentThread().getName() + "正在出售第" + ticket-- + "张票!");

                     }

                }

           }

     }    

}

同步代码块,同步函数

synchronized(obj )

{  

}

同步的好处:解决了线程安全的问题

同步的弊端:相对的降低了效率,在同步代码块的代码每次都会判断

同步的前提:在多线程中,并且使用同一把锁

 

public synchronized void show(){}

同步函数的锁是this,同步代码块的锁是任意对象

 

public static synchronized void show()

静态的同步函数使用的锁是  该函数所属的字节码文件对象

可以用getClass方法获取,也可以使用类名.class表示

 

单例懒汉式在多线程中

class Single

{

     private Single(){}

     private static Single s = null;

     //第一种同步函数方式

     public synchronized static Single getInstance()

     {

            if( s == null)

                 s = new Single();

            return s;

     }

     //第二种同步代码块

     public static Single getInstance2()

     {

            //解决效率问题

            if( s == null)

           {

                 //静态代码块的锁是字节码文件

                 synchronized(Single. class)

                {

                      if( s == null)

                            s = new Single();

                }

           }

            return s;

     }

}

 

面试死锁例子

class MyLock

{

     public static final Object lockA = new Object();

     public static final Object lockB = new Object();

}

class DieLock implements Runnable

{

     private boolean flag;

     public DieLock( boolean flag)

     {

            this. flag = flag;

     }

     public void run()

     {

            if( flag)

           {

                 synchronized(MyLock. lockA)

                {

                     System.out.println( "true-lockA");

                      synchronized(MyLock. lockB)

                     {

                           System. out.println( "true-lockB");

                     }

                }

           }

            else

           {

                 synchronized(MyLock. lockB)

                {

                     System. out.println( "false-lockB");

                      synchronized(MyLock. lockA)

                     {

                           System. out.println( "false-lockA");

                     }

                }

           }

     }

}

public class Demo

{

     public static void main(String[] args)

     {

           DieLock d1 = new DieLock( true);

           DieLock d2 = new DieLock( false);

           Thread t1 = new Thread(d1);

           Thread t2 = new Thread(d2);

           t1.start();

           t2.start();

     }

}  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值