• Java多线程基础知识总结笔记


    本篇笔记记录一些在Java多线程编程中常见的关键字,比较简单和基础的就不写太详细了。

    一、Thread类(其实也是应用了Runnable接口)和Runnable接口(只有一个run方法,应用该类必须重写run方法)

    一般我们定义一个线程类,可以继承Thread类或者应用Runnable接口,不管是继承哪个,都要重写run方法。

    比如我们定义一个线程类:

    public class Threadtest implements  Runnable
    { 
        @Override
        void run()
         {
                 //写你需要这个进程进行的操作
          }
    }

    然后在主方法里面生成线程对象并调用start()方法运行:

    public static void main(String args[]){
                 new Thread(new Threadtest()).start();
       }

    一般来说线程有以下几种状态:

    新生(New):这个状态下我们通过new关键字生成了新的线程Thread对象,为它分配了内存空间,但是这个线程并不是存活状态;

    就绪(Runnable):线程对象调用start()方法,线程进入runnable状态,等待分配CPU资源。这个时候线程是存活的;

    运行(Running):获取CPU资源后,线程开始进行,run()方法被执行,直到线程结束,或者因为资源耗尽等原因被中断或挂起;

    阻塞(Blocked):线程调用join()、sleep()和wait()等方法而暂处阻塞状态;

    死亡(Dead):线程结束,或者因为异常而被迫中断或异常退出,生命周期结束。

    三、sleep方法和wait方法
    3.1、sleep方法(只释放CPU,不释放锁),比如一个变量i,好几个线程都要用到,线程A调用sleep方法只释放了CPU,但是A没有释放i的锁,而那几个线程又刚好需要i达到一定条件才能继续运行,所以线程A就算释放了CPU也没用。

     3.2、wait方法(释放CPU和锁),比如有两个进程,进程A调用wait方法之后,进程释放CPU和锁,要等待线程B调用notify方法或notifyAll方法来通知A,你可以来抢夺资源了,但是A不一定就能抢得到。

    四、synchronized同步标志

    使用场合:比如这里有个资源j,好几个线程都要用到,我们把它叫做竞争资源(竞态条件),对这样的共享资源我们就要进行同步操作,以免一个线程对这个变量的操作被另一个线程覆盖了。通常有两种使用方法:

    1、public/private/protected 函数返回类型 synchronized 函数名(形参)
    {
        //函数内容
    }
    例如:
    public void synchronized FuncName()
    {
        //do something 
    }
    
    2、public/private/protected 函数返回类型  函数名(形参)
    {
            synchronizedthis)
          { 
                 //需要进行同步操作的函数内容
    //其他操作
    }

    下面来个经典的面试题:给个变量j给你,叫你写个两个函数(线程)一个对它进行累加操作,一个对它进行递减操作。然后在主函数里产生四个线程,两个进行累加,两个进行递减。

    public class ThreadTest1
    {
    private int j;
    public static void main(String args[]){
       ThreadTest1 tt=newThreadTest1();
       Inc inc=tt.new Inc();
       Dec dec=tt.new Dec();
       for(inti=0;i<2;i++){
           Thread t=newThread(inc);
           t.start();
               t=new Thread(dec);
           t.start();
           }
       }
    private synchronized void inc(){
       j++;
      System.out.println(Thread.currentThread().getName()+"-inc:"+j);
       }
    private synchronized void dec(){
       j--;
      System.out.println(Thread.currentThread().getName()+"-dec:"+j);
       }
    class Inc implements Runnable{
       public void run(){
           for(inti=0;i<100;i++){
           inc();
           }
       }
    }
    class Dec implements Runnable{
       public void run(){
           for(inti=0;i<100;i++){
           dec();
           }
       }
    }
    }
    View Code

    五、Lock方法和unLock方法
    jdk5.0版本及之前只有synchronized,后来就有了Lock方法,synchronized能做的事情Lock方法都能做,而且锁的获取和释放更加明确,因为编程人员需要明确给出释放锁的时机,是更严谨的机制。
    这个的用法也比较简单,我们先给出下面的类定义,方便解释:

    import java.util.concurrent.locks.*;   
    public class LockTest
    {
        Lock lock = new ReentrantLock();////定义一个公共资源
        private int val = 0;
        //再定义两个私有类,两个类都应用Runnable接口:
        private class IncClass implements Runnable()
        {
             //重写run
             void run()
             {
                   lock.lock();     //锁定
                   try{
                         //对竞争资源的操作
                         val++;
                         System.out.println("现在value的值是"+val);
                   }catch(Exception e){
                   }
                   lock.unlock();      //释放锁
             }
        }
        private class DecClass implements Runnable()
        {
             //重写run
             void run()
             {
                   lock.lock();
                   try{
                         val--;
                         System.out.println("现在value的值是"+val);
                   }catch(Exception e){
                   }
                   lock.unlock();
             }
        }
        //主方法
        public static void main(String[] args) throws Exception
        {
                    LockTest lt = new LockTest();
                    for(int i = 0 ; i< 10 ; i++)
                    {
                         new Thread(lt.new IncClass()).start();
                         new Thread(lt.new DecClass()).start();
                    }
        }
    }
    View Code

    上面的代码没编译运行过,因为直接在文本文档里面敲的,不过也就是那个意思。你要对一个竞态资源进行写操作的时候,先获得锁,这样其他进程没办法对它进行访问,操作完成之后释放锁。
    为了有个确定能运行的例子,去抄了别人写的可以运行的测试代码过来:

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    public class LockTest {
        /**
         * @paramargs
         */
        private int j;
        private Lock lock =new ReentrantLock();
        public static void main(String[] args) {
           // TODO Auto-generated method stub
           LockTest tt = new LockTest();
           for(int i=0;i<2;i++)
           {
               new Thread(tt.new Adder()).start();
               new Thread(tt.new Subtractor()).start();
           }
        }
    
    //两个内部类,作为数据成员的形式出现
        private class Subtractor implements Runnable
    
        {
           @Override
           public void run() {
               // TODO Auto-generated methodstub
               while(true)
               {
                 /*synchronized (ThreadTest.this) {      
                      System.out.println("j--="+ j--);
                      //这里抛异常了,锁能释放吗?
                  }*/
                  lock.lock();
                  try
                  {
                     System.out.println("j--="+ j--);
                  }finally
                  {
                      lock.unlock();
                  }
               }
           }     
      }
    
        private class Adder implements Runnable
        {
           @Override
           public void run() {
               // TODO Auto-generated methodstub
               while(true)
               {
                /*synchronized (ThreadTest.this) {
                  System.out.println("j++="+ j++);
                  }*/
                  lock.lock();
                   try
                  {
                      System.out.println("j++="+ j++);
                  }finally
                  {
                      lock.unlock();
                  }            
               }        
           }
        }
    }
    View Code

    如果是多个线程读取,一个线程进行写的话,只需要给进行写操作的进程锁即可,读取操作那些线程一般是不需要锁的。

     六、join方法

     因为join方法使用方法比较神奇,我看了有篇博文讲得很好,不过看了几遍还是比较晕乎,主要是因为运用得不多,所以理解不深刻。其实学习还是要用任务驱动的方式最高效,这种纯学但是没有运用条件和环境的学习是没什么效果的。

    给出join方法的帮助文档:

     

    它的字面意思是:等当前调用join方法的进程在限定时间内死亡,如果限定时间为0,则一直等待。它的实现是通过一个条件为“this.alive”的“this.wait”循环操作进行的,即是:只要判断这个进程存活,则进行“this.wait”循环,等这个进程终结的时候通过this.notifyAll来通知其他进程争夺资源。
    最后推荐一篇写得还不错的博文,虽然排版有点惨不忍睹,不过内容是不错的:

    http://java.chinaitlab.com/base/828720.html

  • 相关阅读:
    如何制作动态层分组报表
    填报表之数据留痕
    填报表中也可以添加 html 事件
    填报脚本之轻松搞定复杂表的数据入库
    在报表中录入数据时如何实现行列转换
    如何在报表中绘制 SVG 统计图
    如何用报表工具实现树状层级结构的填报表
    6.JAVA_SE复习(集合)
    JAVA_SE复习(多线程)
    数据库基本概念
  • 原文地址:https://www.cnblogs.com/nerohwang/p/3493952.html
Copyright © 2020-2023  润新知