一、线程简介
进程:应用程序执行实力,有独立内存空间和系统资源。
线程:cpu调度和分派的基本单位,进程中执行运算最小的单位,可以独立顺序控制流程
多线程:在一个进程中同时运行了多个线程,完成不同的工作
Win10查看线程:
https://jingyan.baidu.com/article/851fbc37a337023e1f15ab0e.html
二、线程的三种创建方式
1、继承Thread类
2、实现Runable接口(重点)
3、实现Callable接口(了解即可)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @ClassName: ThreadNew
* @Description: 回顾总结线程池的创建
* @author: xiaofei
*/
public class ThreadNew {
public static void main(String[] args) {
new MyThread().start();
new Thread(new MyRunnable()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
new Thread(futureTask).start();
try {
// 获取返回值
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/**
* 继承Thread类
*/
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
/**
* 实现Runnable接口
*/
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable");
}
}
/**
* 实现Callable接口
*/
class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
System.out.println("MyCallable");
return 100;
}
}
小结:
继承Thread类:
子类继承Thread类具备多线程能力
启动线程:子类对象.start()
不建议使用:避免OOP单继承的局限性
实现Runnbale接口:
实现接口Runnable具有多线程能力
启动线程:new Thread(目标对象).start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多线程使用
实现Callable接口:
可以自定义返回值
可以抛出异常
实现比较复杂
三、停止线程
建议正常停止-->利用次数
建议使用标志位-->设置一个标志位
不要使用stop或者destroy
/**
* 测试停止线程
* 1.建议正常停止-->利用次数
* 2.建议使用标志位-->设置一个标志位
* 3.不要使用stop或者destroy
*
* @author xiaofei
* @create 2020-03-02 下午 3:06
*/
public class TestStopThread implements Runnable {
// 1.设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("线程在运行中" + i++);
}
}
// 设置公开方法停止线程
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStopThread testStopThread = new TestStopThread();
new Thread(testStopThread).start();
for (int i = 0; i < 100; i++) {
System.out.println("main" + i);
if (i == 20) {
// 调用stop方法切换标志位,让线程停止
testStopThread.stop();
System.out.println("线程已经停止。。。");
}
}
}
}
四、线程休眠
sleep(时间:单位(毫秒))指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间达到后线程则进入就绪状态;
sleep可以模拟网络延时,倒计时等;
每一个对象都有一个锁,sleep不会释放锁;
五、线程礼让
礼让线程,让当前的正在执行的线程暂停,但不会阻塞;
将线程从运行状态转为就绪状态;
**让CPU重新调度,礼让不一定成功!看CPU心情**
Thread.yield();
六、线程强制执行
Join合并线程,带此线程执行完成以后,再执行其他线程,其他线程会阻塞;
可以想象为插队
Thread thread = new Thread(线程名称);
thread.join();// 插队
七、线程状态观测
线程状态可以处于以下状态之一:
NEW-->尚未启动的线程处于此状态
RUNNABLE-->在Java虚拟机中执行的线程处于此状态
BLOCKED-->被阻塞等待监视器锁定的线程处于此状态
WAITING-->正在等待另一个线程执行特定动作的线程处于此状态
TIMED WAITING-->正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED-->已退出的线程处于此状态
一个线程可以给定时间点处于一个状态,这些状态是不反应任何操作系统线程状态的虚拟机状态。
Thread thread = new Thread(()->{});
Thread.State state = thread.getState();
八、线程的优先级
线程的优先级用数字表示,范围从 1~10.
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改变或者获取优先级:
getPriority() .setPriority(int xxx);
九、守护线程
线程分为**用户线程**和**守护线程**
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如 后台记录操作日志,监控内存,垃圾回收等待...
Thread thread = new Thread(god);
// 默认false: 是用户进程 正常的线程都是用户线程
thread.setDaemon(true);
十、线程同步
由于同一个进程的多个线程共享一块存储空间,在带来方便的同时,也带来了访问冲突问题,为保证数据在方法中被
访问时的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,
使用后释放锁即可。
同步方法:
public synchronized void method(int args){}
缺陷:若将一个大的方法申明为synchronized将会影响效率
同步块:
synchronized (Obj){}
Obj: 同步监视器,Obj可以为任何对象,但是推荐使用共享资源作为同步监视器
同步监视器的执行过程:
第一个线程访问时,锁定同步监视器,执行其中代码。
第二个线程访问时,发现同步监视器已被锁定,无法访问。
第一个线程访问完毕时,解锁同步显示器。
第二个线程访问时,发现同步显示器没有锁,然后锁定并访问。
十一、死锁
多个线程各自占用一些共享资源,并且互相的等待其他线程占有的资源才能运行,而倒是两个或者多个线程都在等待对方释放资源,
都停止执行的情形,某一个同步快同时拥有 两个以上对象的锁时,就可能发生死锁问题。
产生死锁的四个必要条件:
1、互斥条件:一个资源每次只能被一个进程使用。
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3、不削夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
十二、Lock锁
ReetrantLock类实现了Lock,它拥有synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较
常用的是ReentrantLock,可以显式加锁,释放锁。
class TestLock2 implements Runnable {
int tickNums = 10;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 加锁
lock.lock();
if (tickNums > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tickNums--);
} else {
break;
}
} finally {
// 一定要放在finally中释放锁,因为有可能在获取锁的时候抛出异常。
lock.unlock();
}
}
}
}
synchronized与Lock的对比:
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放。
Lock只能锁代码块,synchronized可以锁代码块和方法。
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性。
十三、线程通信
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,直到其他线程通知,与sleep不同,它会释放锁 |
wait(long timeout) | 置顶等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象所有调用wait()方法的线程,优先级别高的线程优先调度 |
当前共有 0 条评论