多线程
一.进程与线程
1.概述
(1)进程
正在运行的程序,我们的电脑上可以有多个进程,在某一时间点上单核CPU只能执行一个进程,CPU可以在多个进程之间可以进行高速切换,人耳和眼睛是感觉不出来的
多进程可以提高CPU的利用率
进程是拥有资源的基本单位
(2)线程
线程:线程需要依赖于进程,进程开启后他会执行很多的任务,把每个任务称之为线程
我们把程序的执行路径只有一条称之为单线程环境,把程序的执行有多条路径,称之为多线程环境
线程是CPU调度资源的基本单位
多线程支持并发和并行
线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
jvm是多线程,至少有俩个线程,一个线程就是主线程,还有一个垃圾回收线程
二.并发与并行
1.概述
(1)并发
指应用能够交替执行不同的任务,并发可以在单处理器和多处理器系统中都存在(每个小时间片执行一个操作,多个操作快速切换执行)
目标:是充分利用处理器的每一个核,达到最高的处理效果
(2)并行
指应用能够同时执行多个不同的任务,并行在多处理器系统中存在
三.实现多线程
1.开启线程的三种方式
方式一
(1)定义一个类,继承Thread类
(2)重写Thread类中的run方法;run方法是需要线程来执行的代码,一般是耗时的
(3)创建我们写的类的对象
(4)开启线程
注意
开启线程不是调用run()方法,调用run()方法,就是你使用了一个对象让这个方法执行了,线程并没有开启;
正确开启线程的方式是调用start()方法开启线程,由线程去调用run()方法.
同一个线程不要多次开启,会抛异常
public static void main(String[] args) {
MyThread myThreadnew =new MyThread();
Thread th1 =new Thread(myThreadnew);
th1.start();
//或者可以写成new MyThread().start();
}
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程执行了");
}
}
方式二(实现接口时还可以继承其他类,灵活性强)
(1)创建一个类,实现Runable接口,重写该接口的run方法
(2)创建Thread类对象,将Runable的子类对象传递进来
(3)调用start()方法开启线程
代码演示:
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.setName("线程A");
thread.start();
}
public class MyRunnable implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"任务执行了");
}
}
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
方式三
(1)创建一个类实现Callable 接口
(2)创建一个FutureTask类将Callable接口的子类对象作为参数传进去
(3)创建Thread类,将FutureTask对象作为参数传进去
(4)开启线程
特点
实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
线程执行完后可以获取线程运算完的结果
执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
代码演示:
//主程序
public class MyCallable implements Callable<Integer> {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(myCallable);
Thread thread = new Thread(task);
thread.start();
//线程执行完之后,可以获取结果
Integer integer = task.get();
System.out.println(integer);
}
//MyCallable类
public class MyCallable implements Callable<Integer> {
//call方法就是线程要执行的方法
@Override
public Integer call() throws Exception {
System.out.println("线程执行了");
int sum=0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
2.几种常用的方法
this.getName()//获取线程名称
thread.setName("哈哈");//设置线程名称
Thread.currentThread().getName()//获取当前线程的名字
thread.getName();//获取主线程名字
thread.sleep(100)//让系统在指定时间段休眠,让线程处于一种阻塞状态
thread.stop();//停止一个线程(废弃方法)
thread.interrupt() //打断线程的一个阻塞状态
thread.join() //等待该线程终止。
//串行: 多线程按顺序自行(将并行改为串行)
//在线程开启之后再调用
thread.getPriority(); //返回线程的优先级。
thread.setPriority(int newPriority); //更改线程的优先级。
thread.yield(): //暂停当前正在执行的线程对象,并执行其他线程。
//多线程礼让,你一下我一下
setDaemon(true)//在线程开启之前可以设为守护线程,主线程(用户线程)结束,守护线程就结束
3.线程优先级
线程调度模型有俩种,分别是分时调度模型和抢占式调度
java使用的是抢占式调度,如果说多个线程的优先级一样,那么线程的执行就会随机抢占,
4.Java用户线程和守护线程
(1)用户线程和守护线程的区别
户线程和守护线程的区别
用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。
(2)用户线程和守护线程的适用场景
由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,适用于辅助用户线程的场景,如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等。
(3)创建守护线程
调用线程对象的方法setDaemon(true),设置线程为守护线程。
注意
1)thread.setDaemon(true)必须在thread.start()之前设置。
2)在Daemon线程中产生的新线程也是Daemon的。
3)不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑,因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。
5.数据安全问题
(1)造成原因
1.多线程环境
2.多个线程在并发操作共享数据
3.有多条语句在操作这个共享数据
(2)解决办法
a.可以使用同步代码块来解决线程安全问题
synchronized(){ //锁,任意对象,放置可能出现线程安全的方法
}//多个线程要共享一把锁
b.可以使用同步代码块来解决线程安全问题
方法上加有synchronized关键字称为同步方法
注意:同步代码块使用任意对象,同步方法使用的锁对象不是任意对象,他用的锁是this,静态同步方法使用的锁对象不是任意对象,他用的锁是当前类的字节码类型
(3)死锁问题
如果出现了同步嵌套,就容易产生死锁问题
死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
代码演示
public class new1 {
public static void main(String[] args) {
test t1 = new test(true);
test t2 = new test(false);
t1.start();
t2.start();
}
}
-------------------------------------------------------------
package org.westos.demo10;
public interface ObjectUntils {
Object objA =new Object();
Object objB =new Object();
}
-------------------------------------------------------------
public class test extends Thread {
boolean flag;
public test(boolean flag){
this.flag=flag;
}
public void run(){
if (flag){
synchronized (ObjectUntils.objA){
System.out.println("objA:true我进来了");
synchronized (ObjectUntils.objB){
System.out.println("objB:true我也进来了");
}
}
}
else{
synchronized (ObjectUntils.objB){
System.out.println("objB:false我也进来了");
synchronized (ObjectUntils.objA){
System.out.println("objA:false我也进来了");
}
}
}
}
}