黑马程序员__线程

1.

进程:正在执行中的程序

线程:进程中一个独立的控制单元。  线程在控制进程的执行

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

主线程: JVM启动的时候会产生一个进程 java.exe,该进程中至少有一个线程负责java进程的执行,这个线程的代码存在于main方法中,称之为主线程。

JVM启动时,还会有负责垃圾回收的线程。

2.创建新线程有两种方法:

一种是将类声明为 Thread 的子类,并重写子类的 run 方法,分配并启动该子类的实例。

步骤:

定义类继承Thread

复写Thread类中的run方法。(将自定义的代码存储在run方法中,让线程运行)

调用线程的 start 方法。  (该方法启动线程,通过调用run) 

3.为什么要覆盖 run()

Thread 用于描述线程,该类定义了一个函数,用于存储线程要运行的代码。该存储功能就是 run 方法。

start();  //开启线程并执行线程的run方法

run(); // 仅仅调用方法,线程创建了但没有运行

4.线程的运行状态:

黑马程序员__线程

5.线程名字获取

System.out.println(Thread.currentThread().getName());     //main

static Thread  currentThread()   返回当前线程对象的引用

getName()  获取线程名称

setName() 或重写构造函数   改变线程名称

6  .创建线程的第2种方式是声明实现 Runnable 接口:

定义一个类,实现Runnable 接口

覆盖Runnable接口中的 run 方法

建立Thread类对象, 将Runnable的子类实例作为实际参数传递给Thread类的构造函数

调用Thread实对象的 start() 方法执行线程

实现方式与继承方式的区别:

实现方式:避免了单继承的局限性,  定义线程时,建议使用此方式

实现方式 线程代码存放在接口的子类的run方法中

继承方式 线程代码存放在Thread子类的run方法中

class Thread2 implements Runnable
{
    private int ticket = 30;
    public void run()
    {
        while(ticket>0)
        {
            if(ticket>0)
                System.out.println("tickete="+ticket--);            
        }
    }
}

class Main
{
    public static void main(String [] args)
    {    
    
        
        Thread2 th2 = new Thread2();
        Thread th = new Thread(th2);
        th.start();
        new Thread(th2).start();
        new Thread(th2).start();
        // 此处多个线程共享资源ticket
        
    }
}

7. 多线程的安全问题:

当多个语句操作同一个线程共享数据,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来,导致共享数据错误。 

java对多线程安全问题提供了专业的解决方式:  同步代码块

synchronized(对象){  需要被同步的代码  }

对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程即使有CPU执行权,也进不去。

同步的前提:   有2个以上的线程;  必须多个线程使用同一个锁;  

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

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

8.同步的另一种方式: 同步函数  

同步函数用的是哪一个锁呢?

函数因为需要被对象调用,那么函数都有一个所属对象引用,就是this,同步函数使用的锁是this

静态同步函数使用的锁是该函数所在类的字节码文件对象  类名.class

class Ticket implements Runnable
{
    private static  int ticket = 30;    
    public void run()
    {    
        while(ticket>0)
        {    
                if(ticket>0)
                {                
                    show();
                }
        }
        
    }
    
    static synchronized void show()  //Ticket.class
    {
        try { Thread.sleep(10); }
        catch(Exception e){}
        System.out.println(Thread.currentThread().getName()+"tickete="+ticket--);
    
    }
    
}

9.单例设计模式

//懒汉式: 单例设计模式,特点:实例延迟加载
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;
    }
}
//饿汉式
class Single
{
    private static final Single s = new Single();
    private Single() {}
    public  static Single getInstance()
    {
        return s;
    }
}

10. 死锁示例:

class Test implements Runnable
{
    public static boolean flag = true;
    
    public void  run()
    {
        if(flag)
        {    
            while(true)
            {
                synchronized(this)
                {   
                    System.out.println("线程1-1");
                    synchronized(Test.class)
                    {
                        System.out.println("线程1-2");
                    }
                }
            }
        }
        else
        {
            while(true)
            {
                synchronized(Test.class)
                {   
                    System.out.println("线程2-1");
                    synchronized(this)
                    {
                        System.out.println("线程2-2");
                    }
                }
            }
        }
        
    }
}

class Main
{
    public static void main(String [] args)
    {
        Test t = new Test();
            
        new Thread(t).start();
        try { Thread.sleep(5); }  catch(Exception e){}
        t.flag = false;
        new Thread(t).start();
        
    }
}

11. 同步唤醒机制:

 wait( )    notify()   notifyAll()  

都使用在同步中,因为要对持有锁的线程操作。  只有同步才具有锁。

wait()会使锁中的线程释放锁。醒来之后,当再次分配到锁时,从刚才的位置继续执行

为什么这些操作线程的方法要定义在 Object 类中?

因为这些方法在操作线程时,都必须要标识他们所操作线程 持有的锁。

只有同一个锁上的被等待线程,可以被同一个锁上的 notify 唤醒, 不可以被不同锁中的notify唤醒。

而锁可以是任意对象,所以可以被任意对象调用的方法定义在 Object 类中。

12. 生产者消费者模式:

对于多个生产者和消费者,为什么定义while 判断标记?    让被唤醒的线程再一次判断标记

为什么定义 notifyAll()   因为用notify可能出现唤醒本方线程的情况,导致程序中所有线程都等待。

JDK1.5中提供了多线程升级解决方案。(显式的锁机制)

将同步 synchronized 替换成现实 Lock 操作,

将 Object 中的wait notify notifyAll 替换成了 Condition 对象,该对象可以由Lock锁获取。

13. 停止线程、 守护线程

停止线程:  stop()方法已过时

一种方法是在 run()方法中结束。 多线程的运行代码通常是循环结构,只要控制住循环,就可以让run 方法结束,也就是线程结束。

特殊情况,当线程处于冻结状态,读取不了标记,run方法无法结束进程,需要  Thread. interrupt()方法:  

当调用了Object类的 wait 方法或者Thread 类的join 、 sleep 方法时,线程阻塞,

interrupt 方法将处于冻结状态的线程强制恢复到运行状态。

setDaemon(boolean on)   守护线程必须在启动线程前启用;  当前台线程都停止时,守护线程自动退出,JVM退出。

14. join 方法

当A线程的代码执行到了 B线程的 .join() 方法时,A就会等待,等B线程都执行完,A才会执行。

join可以用来临时加入线程执行。

15. toString() 
          返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

线程默认优先级是 5 ,最小是1,最大是10。

class Main
{
    public static void main(String [] args)
    {    
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        System.out.println(Thread.currentThread().toString());    
    }
}

优先级为10时,抢到执行权的频率高一些而已,其他线程也有机会抢到。

Thread.yield()  线程执行到yield方法时,暂停正在执行的线程,执行其他线程。

yield方法造成多个线程交替执行,相对避免了一个线程连续多次抢到执行权

16.  某些代码需要被同时执行时,就需要用单独的线程进行封装。