文章目录
一、线程、进程
见操作系统
任务:进程在进入内存之前叫做任务
进程:用户视角进程即程序的一次执行过程,操作系统视角是操作系统分配资源的最小单位,进程包括代码段、数据段和进程控制块PCB,进程有动态性、并发性、独立性、异步性的特征,进程的状态包括就绪态、运行态、阻塞态、就绪挂起态、就绪阻塞态,其中就绪态是已经准备完毕等待cpu代用,阻塞态是等待某些I/O操作,挂起态是把进程从内存拿到磁盘中的状态
线程:一个进程可以分为多个线程,共享进程内的数据,是cpu最小调度单位
二、线程创建
1、Thread类
package Thread;
//继承Thread类,重写run()方法
public class a extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("多线程--"+i);
}
}
public static void main(String[] args) {
a a = new a();
//start()方法开启多线程,会让run()方法和main()方法调度并发执行
//线程开启不一定立即执行,看cpu调度,不确定性
a.start();
//run()方法相当于普通调用函数,按照代码结构顺序执行
// a.run();
for (int i = 0; i < 10; i++) {
System.out.println("主线程--"+i);
}
}
}
concle
输出不定,线程调度执行
2、Runnable接口
package Thread;
//类似Thread子类继承方法,调用Runnable接口,重写run()方法,作为多线程的内容
public class b implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("多线程--"+i);
}
}
public static void main(String[] args) {
b b = new b();
//不同于Thread方法,打开线程,创建Thread的对象,使用Thread的start()实现打开线程
//创建Thread的对象时,把线程类的id现在参数中
// Thread thread = new Thread(b);
//使用thread.start()打开线程
// thread.start();
//上面两句代码可以整合为下面一句代码
(new Thread(b)).start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程--"+i);
}
}
}
Thread继承子类的方法,一个对象就是一个线程
Runnable接口方法,一个对象可以调整对象参数设置为多个线程
3、并发问题
并发问题就是指多个线程并发执行,可能导致的数据访问问题和代码执行顺序问题,cpu一个调度周期内不能完成线程的执行,线程就要等待再次cpu调度执行
package Thread;
public class c implements Runnable {
private int ticket = 10;
@Override
public void run() {
while(true) {
if (ticket < 1) {
break;
}
//这里设计一个延时,减慢程序运行的速度,本质让线程在一个cpu调度周期不能完成,显示并发问题
//就是数据访问问题和执行顺序不确定问题
try {
Thread.sleep(200);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 拿到了第 "+ticket--+"张票!");
}
}
public static void main(String[] args) {
c c = new c();
new Thread(c,"小明").start();
new Thread(c,"老师").start();
new Thread(c,"学生").start();
}
}
concle
结果不确定,就连第i个数据的i都可能不是递减顺序,因为cpu调度时间问题,还可能出现-1的情况
4、Callable类
package Thread;
import java.util.concurrent.*;
public class e implements Callable {
String a;
public e(String a) {
this.a = a;
}
@Override
public Boolean call() throws Exception {
System.out.println(a);
return true;
}
public static void main(String[] args) throws ExecutionException,InterruptedException{
//类似Thread类继承
e e1 = new e("多线程");
e e2 = new e("主线程");
//创建执行服务,并指出进程个数Executors.newFixedThreadPool(进程个数)
ExecutorService ser = Executors.newFixedThreadPool(2);
//提交执行任务
Future<Boolean> result1 = ser.submit(e1);
Future<Boolean> result2 = ser.submit(e2);
//获取结果,根据设计的return值
boolean r1 = result1.get();
boolean r2 = result2.get();
//关闭任务
ser.shutdownNow();
}
}
三、线程管理
1、静态代理
1、静态代理分为真实类和代理类,要指向同一个接口
2、静态代理的意思就是设计一个真实类和代理类,代理类执行主类函数,进而代理对象执行
package Thread;
public class a1 {
public static void main(String[] args) {
b2 b2 = new b2(new b1());
b2.marry();
}
}
interface marry {
void marry();
}
//创建真实角色
class b1 implements marry {
@Override
public void marry() {
System.out.println("婚礼");
}
}
//创建代理角色
class b2 implements marry {
//创建一个真实类的对象,方便函数的调用
b1 target;
public b2(b1 target) {
this.target = target;
}
@Override
public void marry() {
before();
target.marry();
after();
}
void before() {
System.out.println("准备");
}
void after() {
System.out.println("数钱");
}
}
concle
准备
婚礼
数钱
//使用Runnable接口时,Thread就是一个代理,Thread源码本身就指向Runnable接口
2、Lamda表达式
Lamda表达式本质是为了简化代码
1、Lamda表达式只能用于接口定义一个函数的函数式接口
2、Lamda表达式当有一个参数时,可以去掉(),但是当多个参数时,不能去掉()
3、参数前面的类型定义可以去掉,当多个参数时,要么都去掉,要么都不去掉
4、函数部分{}内的代码当只有一行时可以去掉{},多行时不能去掉{}
5、Lamda表达式在定义时,可以使用 接口id 对象id = Lamda表达式
或者 接口id 对象id = null 接着定义Lamda表达式
package Thread;
public class Lamada1 {
public static void main(String[] args) {
//这是正常使用指向接口的类执行类中函数的方式
// Ilove love = new love();
// love.love(2);
//1、Lamda表达式定义时,可以设置为空,接着设置函数
// Ilove love = null;
//2、Lamda表达式定义时,可以直接后面接函数设置
Ilove love = (int a)->{
System.out.println("i love you->"+a);
};
love.love(3);
//1、省去类型定义
Ilove love1 = (a)->{
System.out.println("i love you->"+a);
};
love1.love(521);
//2、省去()
// Ilove love2 = null;
Ilove love2 = a->{
System.out.println("i love you->"+a);
};
love2.love(12);
//省去{},当函数内代码一行
Ilove love3 = a-> System.out.println("i love you->"+a);
love3.love(5);
}
}
//使用Lamda表达式时,只用设置接口,不用定义接口的类,但是接口只能有一个函数
interface Ilove {
void love(int a);
}
//class love implements Ilove {
// @Override
// public void love(int a) {
// System.out.println("i love you->"+a);
// }
//}
concle
i love you->3
i love you->521
i love you->12
i love you->5
3、线程状态
线程状态
线程方法
4、线程停止stop
package Thread_1;
public class a implements Runnable{
//设置一个标志位,当线程满足默写情况时,修改标志为来终止线程
private Boolean flag = true;
@Override
public void run() {
int i =0;
while(flag) {
System.out.println("run Thread->"+i++);
}
}
//自定义的终止函数,修改标志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
a ab = new a();
new Thread(ab).start();
for (int i = 0; i < 100; i++) {
System.out.println("mmain Thread->"+i);
//当线程满足一定情况时,调用终止函数停止线程
if (i==90) {
ab.stop();
System.out.println("线程停止");
}
}
}
}
5、线程休眠sleep
package Thread_1;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.SimpleFormatter;
public class b {
public static void main(String[] args) {
//1、这是根据创建的类倒计时的代码
// try {
// tenDown();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//2、每隔一秒获取当前时间的代码
//获取当前时间
Date datetime= new Date(System.currentTimeMillis());
while(true) {
try{
//使用sleep每隔一秒输出一次当前时间
Thread.sleep(1000);
//输出当前时间,时间格式
System.out.println(new SimpleDateFormat("HH:mm:ss").format(datetime));
//刷新当前时间
datetime = new Date(System.currentTimeMillis());
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public static void tenDown() throws InterruptedException {
int time = 10;
while(true) {
Thread.sleep(1000);
System.out.println(time--);
if (time<=0) {
break;
}
}
}
}
6、礼让线程yield
7、线程强制执行join
jion会让调用jion的多线程获得cpu,先执行
package Thread_1;
//jion调用之前,主线程和多线程的运行随机,不确定性,调用join,调用的多线程运行
public class c extends Thread{
//多线程主体
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("多线程->"+i);
}
}
public static void main(String[] args) throws InterruptedException {
c c = new c();
// Thread thread = new Thread(c);
// thread.start();
c.start();
for (int i = 0; i < 20; i++) {
//调用jion,停止主线程运行,让调用jion的多线程运行到
if (i==10) {
//jion使用要抛出异常
c.join();
}
System.out.println("main线程->"+i);
}
}
}
//class d extends Thread {
// @Override
// public void run() {
// super.run();
// }
//}
8、检测线程状态state
检测线程当前处于什么状态
9、线程的优先级
package Thread_1;
public class e implements Runnable {
@Override
public void run() {
//多线程的id从 Thread-0 计算
System.out.println("多线程 "+Thread.currentThread().getName()+" 的优先级->"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
//输出主线程的默认优先级
//线程的优先级默认是5
System.out.println("主线程的优先级-> "+Thread.currentThread().getPriority());
e e = new e();
Thread e1 = new Thread(e);
Thread e2 = new Thread(e);
Thread e3 = new Thread(e);
Thread e4 = new Thread(e);
Thread e5 = new Thread(e);
Thread e6 = new Thread(e);
//输出多线程的默认优先级
e1.start();
//将多线程的默认优先级设置为最大优先级,10
e2.setPriority(Thread.MAX_PRIORITY);
e2.start();
//先设计优先级再启动
e3.setPriority(1);
e3.start();
e4.setPriority(2);
e4.start();
e5.setPriority(4);
e5.start();
e6.setPriority(6);
e6.start();
}
}
concle
即便线程优先级设计很高,根据操作系统调度原理,不一定按照优先级执行
10、守护线程
public class a {
b b = new b();
Thread thread = new Thread(b);
//默认为false,即用户线程,true变为守护线程
thread.Daemon(true);
thread.start();
}
class b implements Runnable {
}//守护线程在其他线程结束就会停止
四、线程同步
多个线程同时访问同一个资源,需要设计访问顺序和访问条件,防止并发访问导致数据错误
1、线程同步机制
2、线程同步方法
1、synchronized方法就是在方法的定义上添加synchronized修饰符,让整个类的对象变成临界区,见操作系统
2、synchronized块是指将一个对象变成临界区,可以将变化的数据单独设为一个类,在方法调用时,直接在要使用变化数据的代码外面加入一个synchronized的块,就可以单独设置这个块内代码为临界区
package lock;
public class a {
public static void main(String[] args) throws InterruptedException {
int a = 100;
int b = 0;
a2 a2 = new a2(a,b);
a1 a1 = new a1(a2);
Thread thread1 = new Thread(a1,"thread1");
Thread thread2 = new Thread(a1,"thread2");
Thread thread3 = new Thread(a1,"thread3");
thread1.start();
thread2.start();
thread3.start();
}
}
//将变化的数据设置为单独的一个快,在synchronized块方法中,使用
class a2 {
int a;
int b;
public a2(int a,int b) {
this.a = a;
this.b = b;
}
}
//定义多线程的类
class a1 implements Runnable {
// int a;
// int b;
// a1 c = new a1(a,b);
// public a1(int a,int b) {
// this.a = a;
// this.b = b;
// }
a2 a2;
public a1(a2 a2) {
this.a2 =a2;
}
@Override
public void run() {
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void get() throws InterruptedException {
//这是synchronized块的使用方法,操作对象是一个对象,就是变化数据的类的对象
//在函数中找到使用变化数据的代码段,外面加入synchronized块,就能让这个块变成临界区
synchronized (a2) {
if (a2.a<=0){
System.out.println(Thread.currentThread().getName()+" 没钱了");
return;
}
Thread.sleep(1000);
a2.a-=50;
a2.b+=50;
System.out.println(Thread.currentThread().getName()+" "+a2.a);
System.out.println("手里有"+a2.b+"元钱");
}
}
//这是synchronized方法的使用,在方法的定义中加入synchronized的声明,让多线程整个变成临界区
// public synchronized void get() throws InterruptedException {
// if (a<=0){
// System.out.println(Thread.currentThread().getName()+" 没钱了");
// return;
// }
//
// Thread.sleep(1000);
//
// a-=50;
// b+=50;
// System.out.println(Thread.currentThread().getName()+" "+a);
// System.out.println("手里有"+b+"元钱");
// }
}
3、死锁
两种进程因为抢夺同一种资源而相互等待都不执行的情况
注:类中的数据前面加上static之后,当创建两个这个类的对象时,数据可能被覆盖
数据覆盖
package lock;
public class c {
public static void main(String[] args) {
c1 c1 = new c1(1);
c1 c2 = new c1(2);
Thread thread1 = new Thread(c1);
Thread thread2 = new Thread(c2);
thread1.start();
thread2.start();
}
}
class c1 implements Runnable {
//加上static,可能出现数据覆盖
static int a;
c1(int a) {
this.a = a;
}
@Override
public void run() {
System.out.println(a);
}
}
concle
2
2
死锁
package lock;
public class b {
public static void main(String[] args) {
bb b1 = new bb(0,"girl1");
bb b2 = new bb(1,"girl2");
Thread thread1 = new Thread(b1);
Thread thread2 = new Thread(b2);
//模拟死锁
thread1.start();
thread2.start();
}
}
//口红
class b1 {
}
//镜子
class b2 {
}
class bb implements Runnable {
static b1 b1 = new b1();
static b2 b2 = new b2();
int choice;
String girlName;
public bb(int choice,String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
makeup();
}
//synchronized里面嵌套synchronized来构造死锁
//修改死锁的方法:把synchronized的嵌套变成非嵌套
private void makeup() {
if (this.choice == 0) {
synchronized (b1) {
System.out.println(this.girlName+"获得口红");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b2) {
System.out.println(this.girlName+"获得镜子");
}
}
}
else {
synchronized (b2) {
System.out.println(this.girlName+"获得镜子");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b1) {
System.out.println(this.girlName+"获得口红");
}
}
}
}
}
concle
girl1获得口红
girl2获得镜子
4、Lock锁
更像PV操作
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class d {
public static void main(String[] args) {
d1 d1 = new d1();
new Thread(d1).start();
new Thread(d1).start();
new Thread(d1).start();
}
}
class d1 implements Runnable {
private static int a = 10;
//创建一个lock锁类的对象
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void get() throws InterruptedException {
while(true) {
//lock锁一般放在try finally中使用
//try中上锁以及要锁的代码
try{
lock.lock();
if (a>=0) {
try{
Thread.sleep(1000);
System.out.println(a--);
}catch(InternalError e){
e.printStackTrace();
}
}
else
{
break;
}
//finally开锁
}finally {
lock.unlock();
}
}
}
}
五、生产者消费者
package lock;
import java.util.concurrent.locks.ReentrantLock;
public class e {
public static void main(String[] args) {
food food = new food();
food.food = 2;
produce produce = new produce();
customer customer = new customer();
//一个生产者
new Thread(produce,"produce1").start();
//两个消费者
new Thread(customer,"customer1").start();
new Thread(customer,"customer2").start();
// System.out.println(food.food);
}
}
class food {
static int food = 2;
static ReentrantLock lock = new ReentrantLock();
public food() {
this.food = food;
}
}
class produce implements Runnable {
food food = new food();
// int a = food.food;
@Override
public void run() {
while(true) {
// System.out.println(food.food);
try{
food.lock.lock();
if (food.food == 0) {
System.out.println("当前food = "+food.food+++"生产者生产food");
System.out.println("当前food = "+food.food+++"生产者生产food");
}
}
finally {
food.lock.unlock();
}
// synchronized (food) {
//
// }
}
}
}
class customer implements Runnable {
food food = new food();
// static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try{
food.lock.lock();
if (food.food >= 1) {
try {
Thread.sleep(1000);
System.out.println("当前food = "+food.food+",消费者" + Thread.currentThread().getName() + "吃food,当前food = " + --food.food);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally{
food.lock.unlock();
}
}
}
}
同时可以使用线程通信的方法
1、线程通信的方法
2、信号灯法
设置一个标志位,操作系统进程同步的value
3、线程池