目录
Process与Thread
程序:程序是数据和指令的有序集合,本身没有运行的含义,是静态的概念。
进程:是程序执行的过程,它是一个动态的概念,是系统资源分配的单位。
线程:一个进程中包含更多的线程,当然一个进程中至少有一个线程,线程是cpu调度和执行的单位;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5XJ3X4Z-1660287821263)(C:\Users\FBM\AppData\Roaming\Typora\typora-user-images\image-20220708170502917.png)]
线程的创建
继承Thread 2. 实现Runnable 3. 实现Callable
package demo1;
//如何创建多线程
public class strat_thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
System.out.println("-----------我在学习多线程");
}
}
public static void main(String[] args) {
strat_thread01 strat_thread=new strat_thread01();
//这样的话就直接调用run方法
// strat_thread.run();
// 这样的话就是同时开启多线程,里面的for循环是同时执行的。
strat_thread.start();
for (int i = 0; i < 20000; i++) {
System.out.println("------------tongshizaixuexixianchengdeke");
}
}
}
通过线程下载图片的例子
package demo1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习多线程下载
public class strat_thread02 extends Thread{
private String url;
private String name;
public strat_thread02(String url,String name){
this.url=url;
this.name=name;
}
@Override
public void run() {
downloadMethod download=new downloadMethod();
download.downloadMethod1(url,name);
System.out.println("您的图片已经下载完成:"+name);
}
public static void main(String[] args) {
strat_thread02 strat_thread01=new strat_thread02("https://pics5.baidu" +
".com/feed/dbb44aed2e738bd4373c0e80c6341adc267ff9a8.jpeg?token=f2ab22ff3243929b141f525f8c869371",
"image1.jpg");
strat_thread02 strat_thread02=new strat_thread02("https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1657472400&t=2bb1ed82ca4098d830b7a9354a1ca749","image2.jpg");
strat_thread02 strat_thread03=new strat_thread02("https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1657472400&t=cf13b8c38044a363e513c33ed698bb8a",
"image3.jpg");
strat_thread01.start();
strat_thread02.start();
strat_thread03.start();
}
}
class downloadMethod{
// 下载方法
public void downloadMethod1(String url,String file){
try {
FileUtils.copyURLToFile(new URL(url),new File(file));
}catch (IOException e){
e.printStackTrace();
System.out.println("IO异常,downloader方法出现异常!");
}
}
}
您的图片已经下载完成:image1.jpg
您的图片已经下载完成:image3.jpg
您的图片已经下载完成:image2.jpg
这里可以看出来下载出来的图片完成顺序是不一致的
实现Runnable接口
package demo1;
//如何创建多线程
public class strat_thread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
System.out.println("-----------我在学习多线程");
}
}
public static void main(String[] args) {
strat_thread03 strat_thread=new strat_thread03();
new Thread(strat_thread).start();
for (int i = 0; i < 20000; i++) {
System.out.println("------------tongshizaixuexixianchengdeke");
}
}
}
启动首先得new 一个Thread对象,之后把实现重写run方法的对象传入,在开启strat();
继承只能继承一个,而可以实现多个接口,推荐使用这种方法因为可以避免单继承的缺陷。
抢票的例子:
package demo1;
//如何创建多线程
public class strat_thread04 implements Runnable{
private int tiket=10;
@Override
public void run() {
while (true) {
tiket--;
if (tiket <= 0) {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了" + tiket + "张票");
}
}
public static void main(String[] args) {
strat_thread04 strat=new strat_thread04();
// 因为同时在运行同一个线程,所以这里会有抢占资源的情况
new Thread(strat,"小明").start();
new Thread(strat,"黄牛").start();
new Thread(strat,"张三").start();
}
}
龟兔赛跑:
package demo1;
//如何创建多线程
public class strat_thread05 implements Runnable{
private String winner;
@Override
public void run() {
while (true) {
for (int i = 0; i <= 100; i++) {
boolean isTrue =runStept(i);
if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("兔子跑的太快了,休息一下");
}
System.out.println(Thread.currentThread().getName() +"跑了"+i);
if (isTrue){
break;
}
}
System.out.println("胜利者是:"+winner);
return;
}
}
public boolean runStept(int step){
if (winner!=null){
return true;
}else {
if (step==100){
winner=Thread.currentThread().getName();
return true;
}
}
return false;
}
public static void main(String[] args) {
strat_thread05 strat=new strat_thread05();
// 因为同时在运行同一个线程,所以这里会有抢占资源的情况
new Thread(strat,"乌龟").start();
new Thread(strat,"兔子").start();
}
}
实现Callable方法创建线程
package demo2;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//练习多线程下载
public class testCallable implements Callable<Boolean> {
private String url;
private String name;
public testCallable(String url,String name){
this.url=url;
this.name=name;
}
@Override
public Boolean call() throws Exception {
downloadMethod download=new downloadMethod();
download.downloadMethod1(url,name);
System.out.println("您的图片已经下载完成:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testCallable strat_thread01=new testCallable("https://pics5.baidu" +
".com/feed/dbb44aed2e738bd4373c0e80c6341adc267ff9a8.jpeg?token=f2ab22ff3243929b141f525f8c869371",
"image1.jpg");
testCallable strat_thread02=new testCallable("https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1657472400&t=2bb1ed82ca4098d830b7a9354a1ca749","image2.jpg");
testCallable strat_thread03=new testCallable("https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1657472400&t=cf13b8c38044a363e513c33ed698bb8a",
"image3.jpg");
//创建执行服务
ExecutorService es= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> submit1 = es.submit(strat_thread01);
Future<Boolean> submit2 = es.submit(strat_thread02);
Future<Boolean> submit3 = es.submit(strat_thread03);
//获取结果
Boolean aBoolean1 = submit1.get();
Boolean aBoolean2 = submit2.get();
Boolean aBoolean3 = submit3.get();
//关闭服务
es.shutdownNow();
}
}
class downloadMethod{
// 下载方法
public void downloadMethod1(String url,String file){
try {
FileUtils.copyURLToFile(new URL(url),new File(file));
}catch (IOException e){
e.printStackTrace();
System.out.println("IO异常,downloader方法出现异常!");
}
}
}
Callable的好处:
1.可以接受返回值
2.可以抛出异常
缺点:实现起来相比于前面两种方式复杂一点。
/**
* 代理对象的总结:
* 真是对象和代理对象都要实现同一个接口
* 代理对象要代理真实角色
* 好处:
* 代理对象可以做很多真实对象做不了的事情
* 真实对象专注做自己的事情
*/
public class Staticproxy {
public static void main(String[] args) {
// Weddingcompany weddingcompany=new Weddingcompany(new you());
// weddingcompany.HappyMarry();
new Thread(()-> System.out.println("lambada的写法")).start();
new Weddingcompany(new you()).HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//这个是真是对象
class you implements Marry{
@Override
public void HappyMarry() {
System.out.println("卿老师要结婚了");
}
}
//这个是代理对象
class Weddingcompany implements Marry{
private Marry target;
public Weddingcompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry(); //这就是真是的对象
after();
}
public void before(){
System.out.println("结婚之前,收尾款");
}
public void after(){
System.out.println("结婚之后,布置现场");
}
}
Lamda表达式:
函数式接口的定义:
任何接口,如果只包含我以一个抽象方法,那么它就是函数式接口
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
package demo3;
public class lambda {
static class Ilikeimpi1 implements Ilike{
@Override
public void method() {
System.out.println("这是lambda表达式,静态内部类");
}
}
public static void main(String[] args) {
Ilikeimpi ilikeimpi=new Ilikeimpi();
ilikeimpi.method();
//调用静态内部类
Ilikeimpi1 ilikeimpi1=new Ilikeimpi1();
ilikeimpi1.method();
//局部内部类
class Ilikeimpi2 implements Ilike{
@Override
public void method() {
System.out.println("这是lambda表达式,局部内部类");
}
}
Ilikeimpi2 ilikeimpi2=new Ilikeimpi2();
ilikeimpi2.method();
//匿名内部类
Ilike ilike=new Ilike() {
@Override
public void method() {
System.out.println("这是匿名内部类");
}
};
ilike.method();
Ilike ilike2 = ()->{
System.out.println("这是lambda表达式");
};
ilike2.method();
//简化lambda表达式, 如果这里的括号带参数的话是可以直接写参数的,省略括号,只能有一行代码的情况下是可以这样简化的,如果有多行就用代码块包裹
Ilike ilike3=()->System.out.println("简化lambda表达式");
ilike3.method();
}
}
interface Ilike{
void method();
}
class Ilikeimpi implements Ilike{
@Override
public void method() {
System.out.println("这是lambda表达式");
}
}
线程状态:
线程方法:
- 线程停止:
package demo4;
/**
* 停止线程的方法:
* 不建议使用JDK官方提供的停止方法,比如stop,destroy
* 建议使用次数,或者设置标志位去停止。
*/
public class TestStop implements Runnable{
//在这里设置一个标志位
public boolean flag=true;
@Override
public void run() {
int i=1;
while (flag){
i++;
System.out.println(i);
System.out.println("run线程正在运行");
}
}
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);
if (i==900){
System.out.println("线程停止了");
testStop.stop();
}
}
}
}
-
线程休眠:
模拟网络延时,放大问题的发生性
import java.text.SimpleDateFormat; import java.util.Date; /** * 模拟倒计时功能 */ public class testSleep { public static void Sleep() throws InterruptedException { int i=10; while (true){ Thread.sleep(1000); i--; if (i==0){ System.out.println("倒计时结束"); return; } System.out.println("倒计时:"+i); } } public static void main(String[] args) throws InterruptedException { // System.out.println("倒计时开始:"); // Sleep(); while (true){ Date startTime=new Date(System.currentTimeMillis()); Thread.sleep(1000); System.out.println("系统时间为:"+new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime=new Date(System.currentTimeMillis()); } } }
线程礼让Yield:
-
让当前正在执行的线程暂停,但不阻塞。
-
将线程从运行状态转化为就绪状态。
-
让cpu重新调度,礼让不一定成功!!看cpu心情。
package demo4;
public class TestYield {
public static void main(String[] args) {
myYield myYield=new myYield();
new Thread(myYield,"A").start();
new Thread(myYield,"B").start();
}
}
class myYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
}
线程强制执行join:
可以理解为线程插队
public class testJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我是Vip,我要插队"+i);
}
}
public static void main(String[] args) throws InterruptedException {
testJoin testJoin=new testJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 1000; i++) {
if (i==200){
thread.join();
}
System.out.println("mian线程在执行:"+i);
}
}
}
观测线程的状态getState:
public class testState {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束!!!");
});
//观察状态
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(100);
state=thread.getState();//更新线程状态
System.out.println(state);
}
}
}
线程进入死亡状态之后去不能再次启动
线程优先级priority:
-
线程调度器按照线程的优先级决定执行哪个线程
-
线程的优先级用数字表示 1,9 Thread.MIN_PRIORITY=1;
-
使用以下方式改变或获取优先级
getPriority().setPriority(int xxx)
package demo4;
public class TestPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"------》"+Thread.currentThread().getPriority());
myPriority myPriority=new myPriority();
Thread thread1=new Thread(myPriority);
Thread thread2=new Thread(myPriority);
Thread thread3=new Thread(myPriority);
Thread thread4=new Thread(myPriority);
// Thread thread5=new Thread(myPriority);
// thread5.setPriority(11);
// thread5.start();
Thread thread6=new Thread(myPriority);
thread2.setPriority(5);
thread3.setPriority(3);
thread4.setPriority(1);
thread1.setPriority(Thread.MIN_PRIORITY);
thread6.setPriority(Thread.MAX_PRIORITY);
thread6.start();
thread2.start();
thread1.start();
thread4.start();
thread3.start();
}
}
class myPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"------》"+Thread.currentThread().getPriority());
}
}
线程高的不一定会被最先执行,还是要看cpu的调度
守护线程Daemon:
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
-
如,后台记录操作日志,监控内存,垃圾回收等待。。。
public class Daemon {
public static void main(String[] args) {
god god=new god();
you you=new you();
Thread thread=new Thread(god);
thread.setDaemon(true); //在这里设置为守护线程
thread.start();
new Thread(you).start();
}
}
class god implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝一直守护着你");
}
}
}
class you implements Runnable{
@Override
public void run() {
for (int i = 0; i < 35600; i++) {
System.out.println("这是我的线程,一直在运行");
}
System.out.println("我的线程运行完毕,goodbye");
}
}
他的运行结果是:
先设置god线程为守护线程,当我的线程结束运行后,守护线程会后一步我结束而结束。
线程同步synchronized:
并发:同一个对象被多个线程同时操作
抢票事件和银行取钱事件,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程在使用。
在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待使用后释放锁即刻。
但是会存在一下问题:
- 一个线程持有锁会导致其他锁需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,会引起性能问题;
注意:方法里面修改才需要锁,因为锁会影响效率,只读的话不需要。
使用synchronized关键字:
-
在方法上加关键字
public synchronized void buy(){}
-
synchronized同步块,锁的对象是变化的量,需要增删改操作
private double account;
synchronized(account){}
CopyOnWriteArrayList:
这个集合是安全的,而ArrayList是不安全的,想要集合安全可以使用CopyOnWriteArrayList
死锁:
某一个同步块同时拥有两个以上的对象的锁时,并且同时需要对方的资源,然后形成僵持,就可能会发生死锁问题。
public class DeadLock{
public static void main(String[] args) {
Makeup makeup=new Makeup("白雪公主",0);
Makeup makeup1=new Makeup("灰姑凉",1);
makeup.start();
makeup1.start();
}
}
//镜子对象
class mirror{
}
//口红对象
class lipstick{
}
class Makeup extends Thread{
//这里保证只能拿到一份对象,使用static关键字
private static mirror mirror=new mirror();
private static lipstick lipstick=new lipstick();
public String girlFriend;
public int choice;
public Makeup(String girlFriend,int choice){
this.choice=choice;
this.girlFriend=girlFriend;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
//化妆方法
}
public void makeup() throws InterruptedException {
if (choice==0){
synchronized (mirror){
System.out.println(girlFriend+"拿到镜子的锁");
Thread.sleep(1000);
synchronized (lipstick){
System.out.println(girlFriend+"拿到口红的锁");
}
}
}else {
synchronized (lipstick){
System.out.println(girlFriend+"拿到口红的锁");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(girlFriend +"拿到镜子的锁");
}}}}}
如何解决:
public void makeup() throws InterruptedException {
if (choice==0){
synchronized (mirror){
System.out.println(girlFriend+"拿到镜子的锁");
Thread.sleep(1000);
}
synchronized (lipstick){
System.out.println(girlFriend+"拿到口红的锁");
}
}else {
synchronized (lipstick){
System.out.println(girlFriend+"拿到口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){
System.out.println(girlFriend +"拿到镜子的锁");
}
}
}
让两个对象不在同一把锁的下面就可以到了
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
我们只需要破坏其中的一个或者多个就可以避免死锁的情况发生。
LOCK锁:
public class TestLock {
public static void main(String[] args) {
qiangpiao qiangpiao=new qiangpiao();
new Thread(qiangpiao).start();
new Thread(qiangpiao).start();
new Thread(qiangpiao).start();
}
}
class qiangpiao implements Runnable{
public int tiket=10;
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
try {
lock.lock();
while (true){
if (tiket>0){
System.out.println(tiket);
tiket--;
}else {
break;
}
}
}finally {
lock.unlock();
}
}
}
synchronized和lock锁的对比:
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁、synchronized有代码块锁和方法锁
使用Lock锁,,JVM将花费较少的时间来调度线程(性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
生产者消费者模式(线程协作):
package demo6;
public class testPC {
public static void main(String[] args) {
container container=new container();
new producer(container).start();
new customer(container).start();
}
}
//生产的食物
class chicken{
int id;
public chicken(int id) {
this.id = id;
}
@Override
public String toString() {
return "chicken{" +
"id=" + id +
'}';
}
}
//生产者
class producer extends Thread{
container container;
public producer(container container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("生产了"+i+"只鸡");
chicken chic=new chicken(i);
container.push(chic);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者
class customer extends Thread{
container container;
public customer(container container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了"+container.pop().id +"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//缓冲区
class container{
//这里的作用是一直循环将new出来的chicken对象装入
chicken[] chickens=new chicken[10];
int count=0;
//生产鸡肉
public synchronized void push(chicken chicken) throws InterruptedException {
//如果里面满了的话就通知等待
if(count==chickens.length){
this.wait();
}else {
//往里面放入鸡肉
chickens[count]=chicken;
count++;
//这里是通知消费者消费
this.notify();
}
}
public synchronized chicken pop() throws InterruptedException {
//如果消费完了就让消费者等待
if (count == 0) {
this.wait();
}
//往里面放入鸡肉
count--;
chicken chicken1 = chickens[count];
//这里是通知消费者消费
this.notify();
return chicken1;
}
}
信号灯法:
public class testPC1 {
public static void main(String[] args) {
TV tv=new TV();
new player(tv).start();
new watch(tv).start();
}
}
class player extends Thread{
TV tv;
public player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
try {
this.tv.playerMethod("快乐大本营!!!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
this.tv.playerMethod("抖音记录美好生活!!!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class watch extends Thread{
TV tv;
public watch(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
tv.watchMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TV{
String movie;
boolean flag=true;
//表演方法
public synchronized void playerMethod(String movie) throws InterruptedException {
if (!flag){
this.wait();
}
System.out.println("演员表演了:"+movie);
this.notify(); //通知观看
this.movie=movie;
this.flag=!this.flag;
}
//观看方法
public synchronized void watchMethod() throws InterruptedException {
if (flag){
this.wait();
}
System.out.println("观众观看了:"+movie);
this.notify();
this.flag=!this.flag;
}
}
使用线程池:
背景:经常创建和销毁,使用量特别大的资源,比如并发下情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完后在放入到线程池中,可以避免重复的创建和销毁,实现重复利用,类似生活中公共交通工具。
好处:
-
提高了性能(减少了创建新线程的时间)
-
降低资源消耗
-
便于线程管理
corePoolSize: 核心池的大小;
maxmumPoolSize:最大线程数;
keepAliveTime:线程没有任务时最多保持多长时间会停止;
/**
* 测试线程池
*/
public class TestPool {
public static void main(String[] args) {
int i = 10;
ExecutorService service= Executors.newFixedThreadPool(i);
service.execute(new Pool());
service.execute(new Pool());
service.execute(new Pool());
service.execute(new Pool());
service.shutdown();
}
}
class Pool implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}