开始第二遍学习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编写的开源调度框架。线程的生命周期转换图: