爱悠闲 > 黑马学习笔记_多线程

黑马学习笔记_多线程

分类: 黑马学习笔记  |  标签: 多线程,java  |  作者: dddlinux 相关  |  发布日期 : 2015-11-19  |  热度 : 182°

  ——- android培训java培训、期待与您交流! ———-
1. 什么是进程?
通过任务管理器,可以查看到进程,所谓的进程就是正在执行的程序.
2. 多进程的意义?
如果计算机是单进程,那么指定一次执行一个程序.而我们现在的计算机都是多进程. 那么也就是说我们可以一边玩游戏一边听音乐.
提高CPU的使用率.
我们一边 玩游戏一边听音乐.存在两个进程,那么这个两个进程是同时执行的吗?同时: 指的是在一个时间点
不是同时执行的,因此CPU在一个时间点上只能执行一个任务,而我们看到的像是同时执行,其实是这样子的.是CPU在这个两个进程之间
进行高效的切换.

  1. 什么是线程?
    一个应用程序可以执行多个任务,而每一个任务就是一个线程.
  2. 多线程的意义?
    多线程的意义不是提高程序的运行效率,而是提高程序的使用率.
    如何理解这句话呢?
    我们程序在执行的时候其实都是在抢占CPU的时间片(CPU的执行权). 如果一个应用程序只有一个任务,而另一个应用程序有多个任务,那么那个应用
    抢占到CPU的执行权的概率大呢?是多个任务的应用程序抢到的概率比较大.多个任务的应用程序一定就可以抢占到CPU的执行权吗?不一定,所以多线程的执行具有随机性.

    并发和并行的区别:
    并发: 就是在一个时间点上
    并行: 就是在一个时间段上

多线程的2中方式

第一种方式

步骤:
a: 创建一个类,然后让这个类继承Thread
b: 重写run方法
c: 创建该类的对象
d: 启动线程类

代码如下:

class MyThread extends Thread {
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x++){
            System.out.println(x);
        }
    }
}

public class ThreadDemo {   
    public static void main(String[] args) {        
        // 创建对象
        MyThread t1 = new MyThread() ;
        MyThread t2 = new MyThread() ;      
        // 启动线程     
        t1.start() ;
        t2.start() ;    
    }
}
第二中方式

步骤:
a: 创建一个类,然后让这个类去实现Runnable接口
b: 复写run方法
c: 创建定义的类的对象
d: 创建Thread类的对象,然后把C中的对象作为参数传递进来
e: 启动线程

代码如下:

class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 200 ; x++){         System.out.println(Thread.currentThread().getName() + "----" + x);
        }
    }
}

public class ThreadDemo {   
    public static void main(String[] args) {        
        // 创建MyThread的对象
        MyThread my = new MyThread() ;      
        // 创建Thread类的对象,然后把C中的对象作为参数传递进来
//      Thread t1 = new Thread(my) ;
//      Thread t2 = new Thread(my) ;        
        /**
         * 给线程设置名称
         */
        Thread t1 = new Thread(my , "张三") ;
        Thread t2 = new Thread(my , "李四")  ;        
        //启动线程
        t1.start() ;
        t2.start() ;
    }
}

这两种方式: 第二种方式相对比较友好一点点.因为其解决了继承带来的局限性

多线程中常用方法:

public final String getName()
获取线程名称
public final void setName(String name)
设置线程名称
public static Thread currentThread()
通过Thread类直接直接调用这个方法获取当前线程对象

public final int getPriority()
设置线程优先级

public final void setPriority(int newPriority)
获取线程优先级

public static void sleep(long millis)
设置线程休眠

public final void join()
加入线程
意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行
注意事项: 在线程启动之后,在调用方法

public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。

public final void setDaemon(boolean on)
设置线程为守护线程
该方法必须在启动线程前调用。

public final void stop()
停止线程的运行

public void interrupt()
清除该线程的阻塞状态

线程安全问题的产生原因分析

A:首先想为什么出现问题?(也是我们判断是否有问题的标准)

是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据

B:如何解决多线程安全问题呢?

基本思想:让程序没有安全问题的环境。
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。

卖票系统

public class SellTickets implements Runnable {  
    /**
     * 定义票数
     */
    private static int tickets = 100 ;
    @Override
    public void run() {
        while(true){
            if(tickets > 0){
                try {
                    Thread.sleep(100) ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }               System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
            }
        }
    }
}
public class ThreadTest {   
    public static void main(String[] args) {
        // 创建SellTickets对象
        SellTickets st = new SellTickets() ;        
        // 创建Thread对象
        Thread t1 = new Thread(st , "窗口1") ;
        Thread t2 = new Thread(st , "窗口2") ;
        Thread t3 = new Thread(st , "窗口3") ;        
        // 启动线程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

通过以上代码会发现tickets<0,这样会导致卖出0或者-1张票,所以这段代码存在多线程安全隐患

售票系统代码优化如下:

public class SellTickets implements Runnable {  
    /**
     * 定义票数
     */
    private static int tickets = 100 ;
    private static Object obj = new Object() ;

    @Override
    public void run() {
        while(true){
        //加上同步锁
            synchronized(obj){
                if(tickets > 0){
                    try {
                        Thread.sleep(100) ;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }                   System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
                }
            }
        }
    }
}
public class ThreadTest {   
    public static void main(String[] args) {
        // 创建SellTickets对象
        SellTickets st = new SellTickets() ;        
        // 创建Thread对象
        Thread t1 = new Thread(st , "窗口1") ;
        Thread t2 = new Thread(st , "窗口2") ;
        Thread t3 = new Thread(st , "窗口3") ;        
        // 启动线程
        t1.start() ;
        t2.start() ;
        t3.start() ;        
    }
}

最后的总结:

多线程:

进程: 就是正在执行的程序
多进程的意义: 是为了提高CPU的使用率
线程: 
    一个程序中存在多个任务,而每一个任务就是一个线程
多线程的意思: 提高程序的使用率
多线程在执行的时候都是在抢占CPU的时间片(CPU的执行权), 谁抢占到CPU的执行权,CPU就执行谁

JVM的运行原理:

我们使用java启动程序,其实是启动了JVM,这样就相当于启动了一个进程,而在这个进程中自动启动了一个线程
这个线程就是主线程,来通过这个线程调用main方法,执行main方法的方法体

JVM的启动是多线程的吗?

是.jvm在运行的时候至少启动了两个线程,一个是主线程,一个是垃圾回收线程

线程的调度和线程优先级

调度模型:
        分时调度模型:     就是为每一个线程分配指定的时间
        抢占式调度模型:        优先去执行优先级较高的线程
java语言采用的就是抢占式调度模型

线程的优先级:

如何来获取线程的优先级:
    public final int getPriority() 返回线程的优先级。 
如何给线程设置优先级:
    public final void setPriority(int newPriority): 更改线程的优先级。 
线程的优先级存在一个范围: 这个范围是1-10
如果我们没有给线程设置优先级那么使用的就是默认的优先级: 5

线程的控制

休眠 
    public static void sleep(long millis)  throws InterruptedException
加入
    public final void join() throws InterruptedException等待该线程终止。 
注意事项:   必须是在启动之后,在调用该方法
礼让
    public static void yield()暂停当前正在执行的线程对象,并执行其他线程。 

守护
    public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。

    终止
        public final void stop(): 终止线程
        public void interrupt():  中断线程。 

    stop和interrupt的区别:
        stop: 表示的意思是终止该线程
        interrupt: 将线程的阻塞状态中断

创建线程的两种方法:

    第一种方式:
        步骤:
            a: 创建一个类然后让这个类继承Thread
            b: 复写run方法
            c: 创建a中的类的对象
            d: 启动线程, 启动线程使用的是start方法

            可以使用getName方法获取线程名称
            可以使用setName方法设置线程名称
    第二种方式:
        步骤:
            a: 创建一个类,然后让这个类实现Runnable接口
            b: 复写run方法
            c: 创建a中的类的对象
            d: 创建Thread类的对象,把C中的对象作为参数传递进来
            e: 启动线程

            现获取当前正在执行的线程
            Thread.currentThread()然后在调用getName方法获取
            通过构造方法设置名称设置线程名称

卖票的案例

同步代码块:
    synchronized(对象){
        需要被同步的代码 ;
    }
不同代码块保证同步的关键是在这个对象上.要求使用同一个对象. 这个对象看以看做成一个锁.

好处: 可以保证数据的安全性
弊端: 每一个线程都需要判断这个同步锁,所以效率比较低

同步代码块以及同步方法和静态同步方法的锁对象?

同步代码块:  锁是任意的对象
同步方法:   锁对象是this
静态同步方法: 该类对象的字节码文件对象

每一种数据类型都存在一个静态的class属性,可以用来获取当前类的字节码文件对象