JAVAThread 多线程学习

开始第二遍学习java了,发现有好多的知识点在之前学习的时候是不了解的。在java多线程的板块中,学到了除了继承Thread 实现runnable接口以外 还学到了第三种的基于线程池的实现callable接口的线程方式。感觉java真实博大精深。对java线程的笔记总结:

java线程

java程序由一条线程执行完毕 称为单线程程序
java程序由多条程序执行完毕 称为多线程程序

1:多线程 
(1)多线程:一个应用程序有多条执行路径 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径

    多进程的意义?   
        提高CPU的使用率
    多线程的意义?
        提高应用程序的使用率
(2)Java程序的运行原理及JVM的启动是多线程的吗?
    A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
    B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。
(3)多线程的实现方案
A:继承Thread类
B:实现Runnable接口
* 两种方式的比较   

(4)线程的调度和优先级问题
A:线程的调度
  • a:分时调度
  • b:抢占式调度 (Java采用的是该调度方式)
  • B:获取和设置线程优先级
        a:默认是5
        b:范围是1-10
    
(5)线程的控制(常见方法)
A:休眠线程
API方法:

线程睡眠毫秒数

  • public static void sleep(long millis)
B:加入线程
API方法

join:等待线程终止,等待线程执行完毕后其它的线程才可以运行

  • public final void join();
C:礼让线程
API方法

一定程度上让多个线程的执行和谐,不靠谱

  • public static void yield()
D:后台线程
API方法
  • public final void setDaemon(boolean on) 
    当正在运行的线程都是守护线程时,java虚拟机退出,该方法必须在线程启动之前调用 
    标记为守护线程后,线程会依附于某个线程,不会独立的run
E:终止线程(掌握)
API方法
  • public final void stop() 让线程停止,已过时

  • public void interrupt() 终止线程,并且抛出InterruptedException异常,并会执行后续的代码

线程的生命周期(参照 线程生命周期图解.bmp)

A:新建

创建线程对象的过程

B:就绪

有执行的条件和资格,没有执行权

c:运行

有运行的资格,有执行权

D:阻塞

没有执行资格,没有执行权

E:死亡

线程对象变成垃圾,等待被回收

(7)电影院卖票程序的实现
    A:继承Thread类
    B:实现Runnable接口
(8)电影院卖票程序出问题
    A:为了更符合真实的场景,加入了休眠100毫秒。
    B:卖票问题
        a:同票多次
        b:负数票
(9)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
    A:是否有多线程环境
    B:是否有共享数据
    C:是否有多条语句操作共享数据
(10)同步解决线程安全问题
A:同步代码块

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

这里的锁对象可以是任意对象。

B:同步方法

把同步加在方法上。

这里的锁对象是this

C:静态同步方法

把同步加在方法上。

这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)

(11)回顾以前的线程安全的类
  • A:StringBuffer
  • B:Vector
  • C:Hashtable
  • D:如何把一个线程不安全的集合类变成一个线程安全的集合类用Collections工具类的方法即可。
相关知识点
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁
    // 定义锁对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                // 加锁
                lock.lock();
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "张票");
                }
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
    }
(2)死锁问题的描述和代码体现
  • 是指两个或者两个以上的线程在执行的过程中,因争夺资源产一种互相等待现象
  • 同步代码块的嵌套案例
    @Override
public void run() {
    // 死锁代码
    if (flag) {
        // 同步嵌套
        synchronized (MyLock.objA) {
            System.out.println("if obja");
            synchronized (MyLock.objB) {
                System.out.println("if objb");
            }
        }
    } else {
        synchronized (MyLock.objB) {
            System.out.println("else objb");
            synchronized (MyLock.objA) {
                System.out.println("else obja");

            }
        }
    }
}
(3)生产者和消费者多线程体现(线程间通信问题)
    以学生作为资源来实现的

    资源类:Student
    设置数据类:SetThread(生产者)
    获取数据类:GetThread(消费者)
    测试类:StudentDemo

    代码:
        A:最基本的版本,只有一个数据。
        B:改进版本,给出了不同的数据,并加入了同步机制
        C:等待唤醒机制改进该程序,让数据能够实现依次的出现
            wait() 线程等待后立即释放所持有的锁,被唤醒后在等待的位置继续执行
            notify() 唤醒并不代表立马可以执行 线程会转为就绪状态 等待下一次执行
            notifyAll() (多生产多消费)   
  • 为什么定义在Object中?
    • 这些方法的调用通过锁对象调用,而我们使用的锁可能是任意锁对象。所以,这些方法必须定义在Object类中 =* 等待唤醒机制的代码优化。把数据及操作都写在了资源类中
(4)线程组
  • Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 默认情况下,所有的线程都属于主线程组(main组)。 
    返回该线程所属的线程组
  • public final ThreadGroup getThreadGroup() 
    我们也可以给线程设置分组
  • Thread(ThreadGroup group, Runnable target, String name)
    新建线程组代码:

        // ThreadGroup(String name)
        ThreadGroup tg = new ThreadGroup("这是一个新的组");
    
        MyRunnable my = new MyRunnable();
        // Thread(ThreadGroup group, Runnable target, String name)
        Thread t1 = new Thread(tg, my, "林青霞");
        Thread t2 = new Thread(tg, my, "刘意");
    
        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());
    
        //通过组名称设置后台线程,表示该组的线程都是后台线程
        tg.setDaemon(true);
    
(5)线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

  • 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
  • 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
  • JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

public static ExecutorService newCachedThreadPool()

创建一个具有缓存功能的线程池
缓存:百度浏览过的信息再次访问   

public static ExecutorService newFixedThreadPool(int nThreads)

创建一个可重用的,具有固定线程数的线程池

public static ExecutorService newSingleThreadExecutor()

创建一个只有单线程的线程池,相当于上个方法的参数是1   

protected void shutdown() 
顺序关闭线程池中的线程 这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法 
Future<?> submit(Runnable task) 
Future submit(Callable task)

(6)多线程实现的第三种方案

实现Callable接口 步骤和刚才演示线程池执行Runnable对象的差不多。 但是还可以更好玩一些,求和案例演示 好处: 可以有返回值 可以抛出异常 弊端: 代码比较复杂,所以一般不用

匿名内部类方式使用多线程

new Thread(){代码…}.start(); New Thread(new Runnable(){代码…}).start();

多线程的应用
定时器的使用
描述:
  • 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能

  • Timer 一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

public Timer() 
public void schedule(TimerTask task, long delay) 
public void schedule(TimerTask task,long delay,long period) 
* TimerTask 任务类用于为Timer指定任务 public abstract void run() 
public boolean cancel() 
* 开发中 
Quartz是一个完全由java编写的开源调度框架。

线程的生命周期转换图:


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值