• 黑马程序员----java基础笔记中(毕向东)


    11.线程

    a.线程状态图

    b 线程创建 继承 Thread 或者 实现Runnable接口

    c 懒汉单例

    public class Single {
        private static Single s = null;
        private Single(){
            
        }
        
        public static Single getInstance(){
            if(s == null){
                synchronized (Single.class) {
                    if(s == null){
                        s = new Single();
                    }
                }
            }
            return s;
        }
    }

    d 线程死锁  同步中嵌套同步容易发生死锁

    e 多线程运行安全问题

    java解决方案:

    同步代码块

    同步的前提:

    必须有两个或者两个以上的线程;必须是多个线程使用同一个锁

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

    弊端:多个线程需要判断锁,消耗资源


    12.让线程锁一会儿吧

    同步表现形式:

    同步代码块;同步函数

    同步代码块使用的锁是任意java对象;同步函数使用的锁是this;对应static的同步函数,使用的锁不是this。是类型.class该类的字节码文件。

    当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:

          线程A当前持有互斥所锁lock1,线程B当前持有互斥锁lock2。接下来,当线程A仍然持有lock1时,它试图获取lock2,因为线程B正持有lock2,因此线程A会阻塞等待线程B对lock2的释放。如果此时线程B在持有lock2的时候,也在试图获取lock1,因为线程A正持有lock1,因此线程B会阻塞等待A对lock1的释放。二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。

          下面给出一个两个线程间产生死锁的示例,如下:

    package com.itheima;
    
    public class DeadLock {
    	private String objID;
    	public DeadLock(String id){
    		objID = id;
    	}
    	
    	public synchronized void checkOther(DeadLock other){
    		print("entering checkOther");
    		try {
    			Thread.sleep(2000);
    		} catch (InterruptedException e) {
    			// TODO: handle exception
    		}
    	    print("in checkOther() - about to " + "invoke 'other.action()'"); 
    	    other.action();  
            print("leaving checkOther()");  
    	}
    	
    	  public synchronized void action() {  
    	        print("entering action()");  
    	        try { Thread.sleep(500); }   
    	        catch ( InterruptedException x ) { }  
    	        print("leaving action()");  
    	    }  
    	  
    	  public void print(String msg) {  
    	        threadPrint("objID=" + objID + " - " + msg);  
    	    }  
    	  
    	  public static void threadPrint(String msg) {  
    	        String threadName = Thread.currentThread().getName();  
    	        System.out.println(threadName + ": " + msg);  
    	    }  
    	  
    	  public static void main(String[] args) {
    		  final DeadLock obj1 = new DeadLock("obj1");  
    	       final DeadLock obj2 = new DeadLock("obj2");  
    	       
    	       Runnable runA = new Runnable() {  
                   public void run() {  
                       obj1.checkOther(obj2);  
                   }  
               };  
           Thread threadA = new Thread(runA, "threadA");  
           threadA.start();  
           
           try { Thread.sleep(200); }   
           catch ( InterruptedException x ) { }  
     
           Runnable runB = new Runnable() {  
                   public void run() {  
                       obj2.checkOther(obj1);  
                   }  
               };  
           Thread threadB = new Thread(runB, "threadB");  
           threadB.start(); 
           try { Thread.sleep(5000); }   
           catch ( InterruptedException x ) { }  
     
           threadPrint("finished sleeping");  
     
           threadPrint("about to interrupt() threadA");  
           threadA.interrupt();  
           
           try { Thread.sleep(1000); }   
           catch ( InterruptedException x ) { }  
     
           threadPrint("about to interrupt() threadB");  
           threadB.interrupt();  
           
    
           try { Thread.sleep(1000); }   
           catch ( InterruptedException x ) { }  
     
           threadPrint("did that break the deadlock?"); 
           threadPrint("no,已经发生了死锁");
    	}
    }
    

      

     如何在一个线程执行过程中加入另一个线程执行,并使当前线程等待。

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

    调用线程run方法 和start方法的区别,看一段代码然后分析

    class MyThread extends Thread{
        public void run(){
            try {
                Thread.currentThread().sleep(3000);
            } catch (InterruptedException e) {
            }
            System.out.println("MyThread running");
        }
    }
    
    
    
    public class ThreadTest{
        public static void main(String argv[]) {
            MyThread t = new MyThread();
            t.run();
            t.start();
            System.out.println("Thread Test");
          }
    }

    /*
    代码分析过程:

    MyThread t = new MyThread();
    创建了一个线程。

    t.run();
    调用MyThread对象的run方法。
    这是只有一个线程在运行就是主线程。
    当主线程执行到了run方法中的sleep(3000);时。
    这是主线程处于冻结状态。程序并没有任何执行。
    当3秒过后,主线程打印了 MyThread running。 run方法执行结束。

    t.start();
    开启了t线程。
    有两种可能情况。
    第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行,
    打印了Thread Test。主线程结束。
    t线程获取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。
    3秒后,打印MyThread running t线程结束,整个程序结束。

    第二种情况:
    主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。
    就调用自己的run方法。
    指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。
    那么主线程开始执行打印了Thread Test,主线程结束。
    等到3秒后,t线程打印MyThread running ,然后t线程结束。
    程序结束。


    如何停止线程?
    只有一种,run方法结束。
    开启多线程运行,运行代码通常是循环结构。

    只要控制住循环,就可以让run方法结束,也就是线程结束。

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

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

    Thread类提供该方法 interrupt();

     经典线程问题:生产者与消费者 

    对象分析:生产者-消费者-资源

    资源涉及到同步 

    设计资源对象

    class Resource
    {
        private String name;
        private int count = 1;
        private boolean flag = false;
                //  t1    t2
        public synchronized void set(String name)
        {
            while(flag)
                try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
            this.name = name+"--"+count++;
    
            System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
            flag = true;
            this.notifyAll();
        }
    
    
        //  t3   t4  
        public synchronized void out()
        {
            while(!flag)
                try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
            System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
            flag = false;
            this.notifyAll();
        }
    }

    生产者对象 

    class Producer implements Runnable
    {
        private Resource res;
    
        Producer(Resource res)
        {
            this.res = res;
        }
        public void run()
        {
            while(true)
            {
                res.set("+商品+");
            }
        }
    }

    消费者对象

    class Consumer implements Runnable
    {
        private Resource res;
    
        Consumer(Resource res)
        {
            this.res = res;
        }
        public void run()
        {
            while(true)
            {
                res.out();
            }
        }
    }

    测试用例

    class ProducerConsumerDemo 
    {
        public static void main(String[] args) 
        {
            Resource r = new Resource();
    
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
    
        }
    }

    jdk1.5中提供的多线程升级解决方案:

    Lock 和 Condition对象

    import java.util.concurrent.locks.*;
    
    class ProducerConsumerDemo2 
    {
        public static void main(String[] args) 
        {
            Resource r = new Resource();
    
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
    
        }
    }
    
    /*
    JDK1.5 中提供了多线程升级解决方案。
    将同步Synchronized替换成现实Lock操作。
    将Object中的wait,notify notifyAll,替换了Condition对象。
    该对象可以Lock锁 进行获取。
    该示例中,实现了本方只唤醒对方操作。
    
    Lock:替代了Synchronized
        lock 
        unlock
        newCondition()
    
    Condition:替代了Object wait notify notifyAll
        await();
        signal();
        signalAll();
    */
    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();//t1,t2
                this.name = name+"--"+count++;
    
                System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
                flag = true;
                condition_con.signal();
            }
            finally
            {
                lock.unlock();//释放锁的动作一定要执行。
            }
        }
    
    
        //  t3   t4  
        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)
                {
                }
            }
        }
    }

     线程总结:

    wait和sleep区别

    wait:释放cpu执行权,释放同步中锁

    sleep:释放cpu执行权,不释放同步中锁

    停止线程:
    stop过时。
    原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。

    1,定义结束标记。
    2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
    这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
    强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
    但是会发生InterruptedException异常。

    线程中一些常见方法:
    setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,
    只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。

    join():什么意思?等待该线程结束。当A线程执行到了B的.join方法时,A就会处于冻结状态。
    A什么时候运行呢?当B运行结束后,A就会具备运行资格,继续运行。

    加入线程,可以完成对某个线程的临时加入执行。

     yield:临时暂停,可以让线程是释放执行权。


     13.数据类型

    基本数据类型 ,基本数据类型包装类 ,自动装箱拆箱,特殊类型字符串

    Integer m = 128;
    Integer n = 128;

    print("m==n:"+(m==n));false 不同的对象引用

    Integer a = 127;
    Integer b = 127;

    print(a == b);true //当数值在byte范围内(0-127),对于新特性,如果该数值已经存在,则不会在开辟新的空间。

    String s1 = "abc";//s1是一个类类型变量, "abc"是一个对象。
    //字符串最大特点:一旦被初始化就不可以被改变。

    String s2 = new String("abc");

    //s1和s2有什么区别?
    //s1在内存中有一个对象。
    //s2在内存中有两个对象。

    String a = "opq";
    String b = "opq";
    System.out.println("a==b:"+(a==b)); true

    /*
    获取两个字符串中最大相同子串。第一个动作:将短的那个串进行长度一次递减的子串打印。
    "abcwerthelloyuiodef"
    "cvhellobnm"
    思路:
    1,将短的那个子串按照长度递减的方式获取到。
    2,将每获取到的子串去长串中判断是否包含,
    如果包含,已经找到!。
    */

        public static String maxSubStr(String s1, String s2){
            String maxSub = "";
            //得到短的串
            //对短的串依次遍历长度依次递减获取字串  
            String max = s1.length() >= s2.length() ? s1 : s2;
            String min = max == s1 ? s2 : s1;
            for (int i = 0; i < max.length(); i++) {
                for(int j=0,k=min.length()-i ; k != min.length() +1 ;  j++,k++){
                    maxSub = min.substring(j, k);
                    if(max.contains(maxSub)){
                        System.out.println("同时出现的最大字串:"+maxSub);
                        return maxSub;
                    }
                }
            }
            return maxSub;
        }

    StringBuffer 字符串缓冲区

    JDK1.5版本之后出现了StringBuilder

    StringBuffer线程同步

    StringBuilder线程不同步

    开发建议使用StringBuilder

    1.提高效率

    2.简化书写

    3.提高安全性


    14.集合

    /*
    Collection
    |--List:元素是有序的,元素可以重复。因为该集合体系有索引。
    |--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
    |--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
    |--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。


    |--Set:元素是无序,元素不可以重复。、


    List:
    特有方法。凡是可以操作角标的方法都是该体系特有的方法。


    add(index,element);
    addAll(index,Collection);


    remove(index);


    set(index,element);

    get(index):
    subList(from,to);
    listIterator();
    int indexOf(obj):获取指定元素的位置。
    ListIterator listIterator();

    List集合特有的迭代器。ListIterator是Iterator的子接口。

    在迭代时,不可以通过集合对象的方法操作集合中的元素。
    因为会发生ConcurrentModificationException异常。

    所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,
    只能对元素进行判断,取出,删除的操作,
    如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。

    该接口只能通过List集合的listIterator方法获取。

    */

    1,add方法的参数类型是Object。以便于接收任意类型对象。

    2,集合中存储的都是对象的引用(地址)

    |--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。、
    |--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
    HashSet是如何保证元素唯一性的呢?
    是通过元素的两个方法,hashCode和equals来完成。
    如果元素的HashCode值相同,才会判断equals是否为true。
    如果元素的hashcode值不同,不会调用equals。

    注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。


    |--TreeSet:

    Set集合的功能和Collection是一致的。


    15.泛型

    泛型:JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。

    好处
    1.将运行时期出现问题ClassCastException,转移到了编译时期。,
    方便于程序员解决问题。让运行时问题减少,安全。,

    2,避免了强制转换麻烦。


    泛型格式:通过<>来定义要操作的引用数据类型。

    在使用java提供的对象时,什么时候写泛型呢?

    通常在集合框架中很常见,
    只要见到<>就要定义泛型。

    其实<> 就是用来接收类型的。

    当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。


    16.Map集合

    |--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
    |--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
    |--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

    "sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。

    希望打印结果:a(1)c(2).....

    通过结果发现,每一个字母都有对应的次数。
    说明字母和次数之间都有映射关系。

    注意了,当发现有映射关系时,可以选择map集合。
    因为map集合中存放就是映射关系。

    因为打印结果的字母有顺序 核心就是说出使用treemap集合


     17.数组操作工具类、集合操作工具类、静态导入

    高级for循环

    格式:
    for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
    {

    }

    对集合进行遍历。
    只能获取集合元素。但是不能对集合进行操作。

    迭代器除了遍历,还可以进行remove集合中元素的动作。
    如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。

    传统for和高级for有什么区别呢?

    高级for有一个局限性。必须有被遍历的目标。

    建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。

    /*
    JDK1.5版本出现的新特性。

    方法的可变参数。
    在使用时注意:可变参数一定要定义在参数列表最后面。

    */


    18.日期类和文件操作


    19. 文件包装类

    装饰设计模式:
    当想要对已有的对象进行功能增强时,
    可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
    那么自定义的该类称为装饰类。

    装饰类通常会通过构造方法接收被装饰的对象。
    并基于被装饰的对象的功能,提供更强的功能。


    20. 递归  ini文件 properties

    SequenceInputStream

  • 相关阅读:
    解决文字溢出,换行等问题
    js获取年、月、日、时、分、秒
    JQuery EasyUI DataGrid动态合并单元格
    JQuery EasyUI Combobox联动
    JQuery EasyUI 读取设置input
    JQuery EasyUI DataGrid获取当前行索引及快速清空
    jQuery EasyUI combobox多选及赋值
    JQuery EasyUI DataGrid 、tree查询
    HTML元素ID和Name区别
    JQuery EasyUI之DataGrid列名和数据列分别设置不同对齐方式(转)
  • 原文地址:https://www.cnblogs.com/YonguiL/p/4409223.html
Copyright © 2020-2023  润新知