一.进程与线程的关系
1.进程(程序跑起来执行的过程):可以理解为完成一个任务,是由系统分配的。
2.线程:去执行任务的人。
总结:一个进程可以包含多个线程,但至少包含一个线程,不然进程没有存在的意义。
二.核心概念
1.线程状态:new新生》start就绪》线程调度器调用运行》消亡或阻塞
2.当程序运行时即使没有主动创建线程:后台也会有多个线程,如程序的入口main()主线程,gc线程。
3.调度器:一个进程如果开辟了多个线程,线程的运行是由调度器来安排的,且不能干预。
4.优先权:每个线程都有优先权
5.守护线程:
三.创建线程的三种方式
1.继承Thread类:重写run方法,创建线程对象,调用start方法
public class TestThread1 extends Thread{
//重写run方法:线程执行的任务
@Override
public void run(){
for (int i=1; i<=2000; i++){
System.out.println("自定义线程"+i);
}
}
//创建线程对象,调用start方法:启动线程
public static void main(String[] args) {
new TestThread1().start();
}
}
2.实现Runnable接口:重写run方法,创建线程对象后,通过代理对象Thread,调用start方法
public class TestThread2 implements Runnable{
@Override
public void run(){
for (int i=1; i<=2000; i++){
System.out.println("自定义线程"+i);
}
}
//Thread:代理对象
public static void main(String[] args) {
new Thread(new TestThread2()).start();
}
}
3.实现Callable接口并设置返回值类型:重写call方法并抛出异常,创建线程池,提交执行,获取结果并抛出异常,关闭连接池
public class TestThread3 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i=1; i<=2000; i++){
System.out.println("自定义线程"+i);
}
return true;
}
@SneakyThrows
public static void main(String[] args) {
//创建线程对象
TestThread3 testThread3 =new TestThread3();
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> submit =executorService.submit(testThread3);
//获取结果
boolean res = submit.get();
//关闭连接池
executorService.shutdownNow();
}
}
四.线程停止
1.使用标志位,定义一个标志位后利用循环条件,通过修改标志位使线程正常停止
2.不要使用stop或destroy等,jdk不建议的过时方法
public class TestStop implements Runnable{
//标志位
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) {
TestStop testStop =new TestStop();
new Thread(testStop).start();
for (int i=0;i<1000;i++){
System.out.println("主线程循环次数:"+i);
//主线程循环900次时,停止死循环线程
if (i==900){
testStop.stop();
System.out.println("死循环线程停止了");
}
}
}
}
五.线程休眠
public class TestSleep {
public static void main(String[] args) {
down();
}
//模拟倒计时
@SneakyThrows
public static void down(){
int num =10;
while (true){
//线程睡眠时间以毫秒为单位
Thread.sleep(1000);
System.out.println(num--);
if (num<=0){
break;
}
}
}
六.线程礼让
原理:让线程由运行状态转为就绪状态,礼让不一定成功,看cpu线程调度器安排
public class TestYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始");
//线程礼让
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止");
}
public static void main(String[] args) {
TestYield testYield =new TestYield();
new Thread(testYield,"a").start();
new Thread(testYield,"b").start();
}
}
七.线程插队
线程插队:强制执行此线程,其它线程等待,待此线程执行完成后再执行
ublic class TestJoin implements Runnable{
@Override
public void run() {
for (int i=0; i <999; i++) {
System.out.println("自定义线程插队来了"+i);
}
}
@SneakyThrows
public static void main(String[] args) {
TestJoin testJoin =new TestJoin();
Thread thread =new Thread(testJoin);
thread.start();
for (int i=0; i <300; i++) {
//主线程循环200次时,让自定义线程插队
if (i==201){
thread.join();
}
System.out.println("主线程"+i);
}
}
}
八.线程状态
public class TestState {
@SneakyThrows
public static void main(String[] args) {
Thread thread =new Thread(() ->{
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//获取线程状态
Thread.State state = thread.getState();
System.out.println("新生状态"+state);
//观察启动后状态
thread.start();
state = thread.getState();
System.out.println("就绪状态:"+state);
//观察阻塞状态-就绪状态-消亡状态
while (state !=Thread.State.TERMINATED){
Thread.sleep(1000);
state=thread.getState();
System.out.println("阻塞状态-就绪状态-消亡状态:"+state);
}
}
}
九.线程优先级
原理:线程调度器会根据优先级来“权重”先调度那个线程,优先级最大10-1最小,默认5
public class TestPriority implements Runnable{
@Override
public void run() {
System.out.println("名字"+Thread.currentThread().getName()+"优先级"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
TestPriority testPriority =new TestPriority();
Thread t1 =new Thread(testPriority);
Thread t2 =new Thread(testPriority);
Thread t3 =new Thread(testPriority);
//默认优先级:NORM_PRIORITY=5
t1.start();
//最大优先级:MAX_PRIORITY=10
t2.setPriority(Thread.MAX_PRIORITY);
t2.start();
//最小优先级:MIN_PRIORITY=1
t3.setPriority(Thread.MIN_PRIORITY);
t3.start();
}
}
十.守护线程
要领:守护线程需在启动前设置,否则无效
案例:主线程先消亡情况,但只要有一个用户线程未消亡,守护线程就会存活。
public class TestDaemon {
public static void main(String[] args) {
Thread thread =new Thread(new Guard());
thread.setDaemon(true);
thread.start();
new Thread(new User()).start();
for (int i = 0; i < 50; i++) {
System.out.println("主线程心跳");
}
}
}
//守护线程
class Guard implements Runnable{
@Override
public void run() {
while (true){
System.out.println("守护线程心跳");
}
}
}
//用户线程
class User implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("用户线程心跳");
}
}
}
十一.多线程不安全案例
我们都知道ArrayList线程不安全:那就用多线程去操作ArrayList看看结果
public class TestArrayList {
@SneakyThrows
public static void main(String[] args) {
List<String> list =new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(2000);
//由于线程不安全:集合长度可能是10000,9999、9998、9997....
System.out.println("集合长度:"+list.size());
}
}
十二.同步方法与同步块
同步锁:多线程访问同一个资源,加锁后会排队访问,同一时间内只有一个线程可以进行操作。
public class TestArrayList implements Runnable{
/**
* synchronized修饰的方法叫同步方法,作用域this,锁的是当前方法归属类的对象
* 所以它锁的是TestArrayList类,而不是list集合
*/
@SneakyThrows
@Override
public synchronized void run() {
List<String> list =new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(2000);
System.out.println("集合长度:"+list.size());
}
public static void main(String[] args) {
new Thread(new TestArrayList()).start();
}
}
public class TestArrayList implements Runnable{
@SneakyThrows
@Override
public void run() {//任务:创建一万个线程,往list里插入一万条数据
List<String> list =new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
//同步块:可以锁任何变化的量,使用场景增删改
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(2000);
System.out.println("集合长度:"+list.size());
}
public static void main(String[] args) {
new Thread(new TestArrayList()).start();
}
}
十三.死锁
死锁发生条件:
1.多线程互相持有对方的锁,
2.资源只有一份出现僵持,需等待资源后才能运行,
此时会出现线程停止的死锁问题。
package com.baijun.utils.entity;
import lombok.SneakyThrows;
/**
* 死锁:多线程互相持有对方的锁,且资源只有一份出现僵持需要等待拿到对方的资源后才能运行,从而出现线程停止的死锁问题。
*/
public class TestLock extends Thread{
//资源只有一份
static Lipstick lipstick =new Lipstick();
static Mirror mirror =new Mirror();
//选择
int choice;
//使用化妆的人
String girlName;
TestLock(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
@Override
public void run(){
//化妆
makeup();
}
//化妆,互相持有对方的锁,需要拿到对方的资源
@SneakyThrows
private void makeup(){
if (choice==0){
synchronized (lipstick){//口红锁
System.out.println(girlName+"口红锁");
Thread.sleep(1000);
synchronized (mirror){//镜子锁
System.out.println(girlName+"镜子锁");
}
}
}else {
synchronized (mirror){//镜子锁
System.out.println(girlName+"镜子锁");
Thread.sleep(2000);
synchronized (lipstick){//口红锁
System.out.println(girlName+"口红锁");
}
}
}
}
public static void main(String[] args) {
TestLock testLock1 =new TestLock(0,"宝子");
TestLock testLock2 =new TestLock(1,"小凤");
testLock1.start();
testLock2.start();
}
}
//口红
class Lipstick{}
//镜子
class Mirror{}
十四.lock锁
package com.baijun.utils.entity;
import lombok.SneakyThrows;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock:是显式锁需要手动开关,synchronized是隐式锁,出了作用域自动释放。
* 优先顺序:Lock、同步代码块、同步方法
*/
public class TestLock2 {
public static void main(String[] args) {
TestLock3 testLock3 =new TestLock3();
for (int i = 0; i < 3; i++) {
new Thread(testLock3).start();
}
}
}
class TestLock3 implements Runnable{
int ticketNums =10;// 票数
//定于Lock锁:ReentrantLock可重入锁
private final ReentrantLock lock =new ReentrantLock();
@SneakyThrows
@Override
public void run() {//买票
while (true){
try {
lock.lock();//加锁
if (ticketNums>0){
Thread.sleep(1000);
System.out.println(ticketNums--);
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}