首页 > 代码库 > 线程知识总结

线程知识总结

进程就是一个正在运行的程序,分配内存让应用程序能够运行。

 

       Windows系统号称多任务(可以同时运行多个应用程序)。

           宏观上看:Windows确实是运行多个程序。

           微观上看:CPU快速切换执行任务,由于速度特别快,我们人感觉不到这个切换的过程

 

线程在一个进程中负责代码的执行,就是进程中的执行路径。

  

 疑问:没有主线程,为什么代码可以执行?

        java程序在运行的时候,jvm会帮我们创建一个主线程来执行代码。主线程主要负责main方法中的代码执行。

   

 一个java程序中至少有2个线程

       一个主线程主要负责main方法中的代码执行,一个垃圾回收器线程,负责垃圾回收。

   多线程:在一个进程中多个线程同时执行不同的任务。

   “同时”:单核CPU快速切换多个线程执行任务

            速度特别快,人感觉不到切换。

 

多线程的好处:

       1.解决一个进程中同时执行多个任务的问题、

       2.提高资源的利用率。

  

多线程的弊端:

       1.增加CPU的负担。线程不是越多越好。

       2.降低了一个进程中线程的执行效率。

       3.容易引发线程安全问题。

       4.出现死锁现象。

 

 java中创建线程有2种方式

      线程的定义方式一Thread (线程类)

        1.需要定义一个类来继承Thread类。

        2.重写thread类中run方法,把自定义线程的任务代码写在run方法中。

               * 每一个线程都有自己的任务代码,jvm创建的主线程任务代码就是main方法,

               * 自定义的线程的任务代码就写在run方法中,自定义的线程就需要执行run方法中的代码。

        3.创建Thread的子类,并且调用start方法开启线程。

           

     注意点:一旦线程开启了,会默认执行线程对象中的run方法,但是千万不要自己直接调用run方法,如果直接调用了run方法,就和普通方法没有区别。

 

    Java 是单线程,如果一个类已经继承了别的类,这个时候就不能够通过继承thread类来创建线程了。

  

线程的使用细节:

       1.线程的启动使用父类的start()方法

       2.如果线程对象直接调用run(),那么JVm不会当作线程来运行,会认为是普通的方法调用。

       3.线程的启动只能由一次,否则抛出异常

       4.可以直接创建Thread类的对象并启动该线程,但是如果没有重写run(),什么也不执行。

       5.匿名内部类的线程实现方式

 

   线程的定义方式二:

     1.自定义一个类实现Runable接口  , 接口中就会提供一个run方法

     2.实现Runable接口中的run方法。将线程中的任务写在run方法中

     3.创建Runable接口的实现类对象

       注意点:实现runnable接口的类不是一个实现类,他是没有能力开启线程的只有是thread或者他的子类才是线程类,只有他们才有开启线程的能力。

     4.创建一个Thread对象,并把Runable实现类创建的对象作为参数。

     5.调用Thread对象的start方法来开启线程

  

 问题 为什么要将Runable接口实现类的对象作为参数传递?

      为了让对象中的run方法能够在线程中的run方法中执行。也就是能够将对象中的run方法最为线程中的任务来执行

     

 *  推荐使用 :第二种方式  实现Runable.

 

 *  java 是单继承 ,多实现的

    售票的线程方式一(thread):

 

class SaleTickets extends Thread {
    static int num = 50; // 总票数 共享的数据,三个窗口同时操作同一份数据
    //static Object o = new Object();
    public SaleTickets (String name) {
        super(name);
    }
   // 重写run方法:卖票的任务
    public void run() {
       
        while (true) {
            synchronized ("hh") { //任意类型的对象,锁对象应该是同一个对象
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                if(num > 0) {
                    System.out.println(this.getName() + "卖了第" + num + "票");
                    num--;
                
                } else {
                    System.out.println("票已售完");
                    break;
                }
            }
        
            
        }
    }
    
}
public class Demo4 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
       SaleTickets t1 = new SaleTickets("窗口1");
       SaleTickets t2 = new SaleTickets("窗口2");
       SaleTickets t3 = new SaleTickets("窗口3");
       t1.start();
       t2.start();
       t3.start();
    }

}

 

售票的线程方式二(Runnable):

class SaleTickets1 implements Runnable {
    static int num = 50;
     public void run() {
         while (true) {
             synchronized ("djj") {
                 if (num > 0) {
                        System.out.println(Thread.currentThread().getName() + "卖出第" + num + "张票");
                        num--;
                    } else {
                        System.out.println("票已售尽");
                         break;
                    }
             }
            
        }
        
    }
}
public class Demo8 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建实现类对象
       SaleTickets1 st = new SaleTickets1();
       // 创建三个线程对象
       Thread t = new Thread(st, "窗口1");
       Thread t1 = new Thread(st, "窗口2");
       Thread t2 = new Thread(st, "窗口3");
       t.start();
       t1.start();
       t2.start();
    }

}

 

java给线程加锁 :

 

解决线程安全问题的手段:

 

通过同步代码的方式:

 

    方式一:同步代码块

 

       锁对象可以是任意一个java中的对象

 

        java中的任意一个对象都会有一个对象的状态 ,就可以通过对象的状态来作为锁的一个标识符。

 

     state = 0 表示锁是关闭   state = 1 表示锁打开。

 

       synchronized (锁对象) {

   }

 

同步代码块的使用注意点

 

         1.任意一个对象都可以做锁对象

 

        2.如果你在同步代码块中调用了sleep方法 ,不会释放锁对象

 

       3.只有真正存在线程安全的时候才需要使用同步代码块,否则会降低执行效率

 

       4.多线程操作锁对象必须是唯一的 ,否则无效

 

方式二:同步函数

 

       写法:用synchronize来修饰方法。

 

       如果同步函数是一个非静态的函数:锁对象就是调用者对象。

 

       如果同步函数是一个静态的函数:锁对象同步函数是所在类的字节码(Class对象)

 

同步函数和同步代码块的区别:

 

       1.同步代码块,可以随意确定同步代码的范围,同步函数只能同步整个函数的代码。

 

       2.同步代码块的锁对象可以自己任意指定,同步函数的锁对象是固定的。

 

不知道什么时候安全什么时候不安全。

 

出现线程安全的问题根本原因:

 

     1.存在两个或两个以上的线程。并且线程之间共享着一个资源。

 

     2.多个语句操作了共享资源

 

线程死锁:

 

 但是如果使用不当会导致线程死锁问题:

  A线程等B线程完成, B线程又在等A线程   结果两个人就一直等下去了 ,这个时候就造成了线程死锁。

  线程死锁不一定会出现,有可能会出现。

  死锁现象的解决方案 :  没有方案 ,尽量避免发生。

 

实例:

class DeadLock extends Thread {
    public DeadLock(String name){
        
        super(name);
    }
    
    @Override
    public void run() {
        
        
        if("张三".equals(Thread.currentThread().getName())){
            
            synchronized ("遥控器") { //锁对象就锁住了
                
                System.out.println("张三拿到了遥控器 ,准备去拿电池");
                
                synchronized ("电池") {//已经被锁住了
                    
                    System.out.println("张三拿到了遥控器和电池,开着空调,在也 不冷了");
                }
            }
        }else if("老王".equals(Thread.currentThread().getName())){
            
            synchronized ("电池") { //锁也被锁住了  电池对象的状态 变为锁住的状态
                
                System.out.println("老王拿到电池,准备去拿遥控器");
                
                synchronized ("遥控器") { //遥控器也被锁住了
                    
                    System.out.println("老王拿到了遥控器和电池,开着空调,在也 不冷了");
                }
            }
        }
        
    }
    
}

public class Demo6 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        DeadLock t1 = new DeadLock("张三");
        t1.start();
        DeadLock t2 = new DeadLock("老王");
        t2.start();
    

 

线程的通讯:

  线程的通讯:一个线程完成自己的任务,去通知另外一个线程去完成另外一个任务。

 

    wait();  等待      如果线程执行了wait方法 ,那么该线程就会处于一个等待状态,等待状态的线程必须要通过其他线程来调用

    notify()方法来唤醒。

 

    notify();唤醒   随机唤醒线程池中的一个线程。

     notifyAll(); 唤醒所有等待的线程。

 

 waitnotify的使用注意点 :

   1.wait方法和notify方法是属性Object对象

   2.wait方法和notify方法必须在同步线程中执行

   3.wait方法和notify方法必须有锁对象来调用

 

消费者和生产者之间的通讯:

class Product {
    
    String name;
    double price;
    
}

class Producter extends Thread{  //生产产品
    Product p;
    
    public Producter(Product p){
        
        this.p = p;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        
        for(int i = 0;i<50;i++){
            
            synchronized (p) {
                
                if(i % 2 == 0){
                    p.name = "苹果";
                    p.price = 3.5;
                    System.out.println(p.name+":"+p.price);
                }else {
                    
                    p.name = "香蕉";
                    p.price = 2.0;
                    System.out.println(p.name+":"+p.price);
                }
                
                //产品以经生产出来 唤醒消费者来消费
                p.notify();
                
                try {
                    p.wait(); // 等待消费者提醒
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                
            }
        }
        
    }
}

class Custem extends Thread{
    
    Product p; // 消费者消费的产品
    
    public Custem(Product p){
        
        this.p = p;
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        
        for (int i = 0;i<50;i++){
            synchronized (p) {
                //通过锁对象来调用
                if(p.name != null){
                    System.out.println("吃"+p.name+":"+p.price);
                } // 没有产品  告诉生产者需要生产产品
                    
                //唤醒生产者
                p.notify();
                try {
                    p.wait();  //等待产品创建
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

public class Demo9 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建一个产品对象
        Product p = new Product();
        
        //创建一个生产者
        Producter t1 = new Producter(p);
        
        //创建一个消费者
        Custem t2 = new Custem(p);
        
        t1.start();
        t2.start();
        

    }

}

 

线程知识总结