文章目录
- 进程
- 线程
-
- 线程与进程
- Java运行原理
- JVM启动的线程
- 多线程实现方式(继承Thread)
-
- 多线程实现方式(实现Runnable接口)
- 多线程实现方式(实现Callable接口)
- 线程安全问题
-
进程
进程概述
进程就是正在运行的程序 是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
多进程意义
单进程计算机只能做一件事
多进程计算机可以同时做很多事
多进程不是提高执行速度 而是提高CPU的使用率
线程
线程概述
在一个进程内部又可以执行多个任务 而这每一个任务我们就可以看成是一个线程
线程是程序使用CPU的基本单位 所以线程是CPU调度的基本单位
多线程意义
多线程的作用不是提高执行速度 而是为了提高应用程序的使用率
多线程中哪个线程能抢占到CPU资源不是确定的 所以多线程具有随机性
并发和并行
并发
指应用能够交替执行不同的任务
就是在几乎不可察觉的速度不断去切换执行任务
是逻辑上同时发生
并行
指应用能够同时执行不同的任务
是物理上同时发生
线程与进程
线程和进程
进程是一个应用程序
线程是一个进程中的子集(执行单元)
区别
进程:
是系统中正在执行的一个应用程序
程序一旦运行就是进程
是资源分配的最小单位
线程:
是系统分配处理器时间资源的基本单元
是进程之内独立执行的一个单元执行流
是程序执行的最小单元
Java运行原理
Java.exe命令会启动java虚拟机 启动JVM 等于启动了一个应用程序 也就是启动了一个进程
该进程会自动启动一个 主线程 然后主线程去调用某个类的 main 方法
所以main方法运行在主线程中
JVM启动的线程
JVM启动至少启动了垃圾回收线程和主线程
所以JVM启动的线程 是多线程的
多线程实现方式(继承Thread)
线程是依赖于进程而存在的 我们应该先创建一个进程(JVM)出来
而进程是由系统创建出来的 所以我们应该调用系统功能创建 一个进程
Java不能直接去调用系统功能 所以我们不能直接去实现多线程程序
继承Thread类
格式:
public class MyTest {
public static void main(String[] args) {
//创建线程对象
MyThread myThread = new MyThread();
//启动线程
myThread.start();
}
}
//继承Thread类
class MyThread extends Thread {
//重写run()方法
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
设置、获取线程优先级
public final void setPriority(int newPriority)
设置线程的优先级 范围是1---10 默认优先级是5
public final int getPriority()
获取线程的优先级
(线程并不是按照优先级高的线程执行
线程优先级的大小仅仅表示这个线程被CPU执行的概率增大了
多线程具有随机性)
线程控制
1.休眠线程
public static void sleep(long millis)
线程休眠(毫秒值)
2.加入线程
public final void join()
等待该线程执行完毕后 其他线程才能被执行
在线程启动之后 在调用该方法
3.礼让线程
public static void yield()
暂停当前正在执行的线程对象 并执行其他线程
(礼让线程是暂停正在执行的线程 暂停的时间相当短
如果这个线程暂停之后 其他线程还没有抢占到CPU的执行权
这个时候这个线程应该再次和其他线程抢占CPU的执行权)
4.守护线程
public final void setDaemon(boolean on)
把该线程标记为守护线程
该方法在启动线程前调用
5.中断线程
public final void stop()
停止线程的运行
public void interrupt()
中断线程
(线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞)
Java用户线程和守护线程
1.用户线程和守护线程的区别
用户线程和守护线程都是线程
区别在Java虚拟机在所有用户线程dead后 程序就会结束 不管是否还有守护线程还在运行 若守护线程还在运行 则马上就会结束
2.用户线程和守护线程的适用场景
守护线程不适用于输入输出或计算等操作 因为用户线程执行完毕后 程序就会dead 适用于辅助用户线程的场景 如:JVM垃圾回收 内存管理
3.创建守护线程
调用线程对象的方法setDaemon(true) 设置线程为守护线程
1)thread.setDaemon(true)必须在thread.start()之前设置
2)在Daemon线程中产生的新线程也是Daemon的
3)不是所有的应用都可以分配给Daemon线程来进行服务 比如读写操作或者计算逻辑
因为Daemon Thread还没来得及进行操作 虚拟机可能已经退出了
多线程实现方式(实现Runnable接口)
实现Runnable接口
这种方式的扩展性更强 实现一个接口 还可以在去继承其他类
好处:
避免了由于Java单继承带来的局限性
格式:
public class MyTest {
public static void main(String[] args) {
//创建线程对象
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
//启动线程
thread.start();
}
}
//实现Runnable接口
class MyRunnable implements Runnable {
//重写run()方法
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
多线程实现方式(实现Callable接口)
实现Callable接口
1.相较于实现Runnable接口 方法可以有返回值 并且可以抛出异常
2.执行Callbale方式 需要FutureTask实现类的支持 用于接收运算结果
FutureTask是Future接口的实现类
实现步骤:
1.创建一个类实现Callable接口
2.创建一个FutureTask类将Callable接口的子类对象作为参数传进去
3.创建Thread类,将FutureTask对象作为参数传进去
4.开启线程
格式:
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程对象
MyCallable myCallable = new MyCallable();
//创建FutureTask对象
FutureTask futureTask = new FutureTask(myCallable);
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
//获取返回值
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 1; i < 10; i++) {
num += i;
}
return num;
}
}
线程安全问题
产生的原因:
多线程环境
共享数据
多条语句操作共享数据
解决问题:
把多条语句操作共享数据的代码锁起来
让任意时刻只能有一个线程执行即可
使用同步代码块
//不能在括号里面直接new对象
synchronized(对象){
要被同步的代码;
}
同步代码块、方法
同步代码块格式:
锁对象是任意一个对象
//不能在括号里面直接new对象
//同步代码块上的锁是一个互斥锁
synchronized(对象){
要被同步的代码;
}
好处:
同步的出现解决了多线程的安全问题
弊端:
当线程多的时候 每个线程都会判断同步上的锁 很耗费资源 降低了程序的运行效率
同步方法
public synchronized void m(){}
锁对象是this
静态同步方法
public static synchronized void m(){}
锁对象是静态方法所在类对应的字节码文件对象
Lock锁
概述
可以更清晰的表达加锁和释放锁
ReentrantLock类
void lock() 加锁
void unlock() 释放锁
格式:
class X {
private final ReentrantLock lock = new ReentrantLock();
public void m() {
lock.lock();
// block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
卖票问题
public class SellTicketsTest {
public static void main(String[] args) {
//需求:某电影院目前正在上映贺岁大片 共有100张票
//而它有3个售票窗口售票 请设计一个程序模拟该电影院售票
//创建线程对象
SellTickets sellTickets = new SellTickets();
Thread thread1 = new Thread(sellTickets);
Thread thread2 = new Thread(sellTickets);
Thread thread3 = new Thread(sellTickets);
//启动线程
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTickets implements Runnable {
//总票数
static int tickets = 100;
//重写run()方法
@Override
public void run() {
try {
sell();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//同步方法
public synchronized void sell() throws InterruptedException {
while (true) {
if (tickets <= 0) {
System.out.println("票卖完了");
break;
}
Thread.sleep(50);
System.out.println(Thread.currentThread().getName() + "正在售票 还有" + (--tickets) + "张票");
}
}
}
死锁
概述
如果出现同步嵌套 就容易产生死锁问题
是指两个或者两个以上的线程在执行的过程中 因争夺资源产生的一种互相等待现象
两个或者两个以上的线程 在抢占CPU的执行权的时候 都处于等待状态
public class MyTest04 {
public static void main(String[] args) {
//创建线程对象
Deadlock d1 = new Deadlock(true);
Deadlock d2 = new Deadlock(false);
//启动线程
d1.start();
d2.start();
}
}
class Deadlock extends Thread {
//创建两个对象锁
static Object objA = new Object();
static Object objB = new Object();
private boolean flag;
public Deadlock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag){
synchronized (objA){
System.out.println("true---objA");
synchronized (objB){
System.out.println("true---objB");
}
}
}else {
synchronized (objB){
System.out.println("false---objA");
synchronized (objA){
System.out.println("false---objB");
}
}
}
}
}