Java多线程

多线程是为了提高程序执行效率
进程是一个应用程序
线程是一个进程的执行场景/执行单元
一个进程可以启动多个线程
JVM就是一个进程 JVm先启用一个线程调用main方法 同时在启用一个垃圾回收线程负责看护 …
进程所用的栈内存独立 但是堆内存和方法区共享
main()方法结束只是主线程结束 其他线程可能还在执行
单核CPU只有一个大脑:不能做到真正的多线程并发 但是可以给人一种“多线程并发”的感觉
多线程并发就是 每个线程自己处理自己的

多线程实现 =======第二种方法更好 面向对象
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
第一种 编写一个类 直接继承java.lang.Thread 并重写run方法
public class step01 {
public static void main(String[] args) {
MyThread myThread=new MyThread();

    //myThread.run();  如果这样写 不会开辟分支线程 不会开辟新的栈  还是单线程

    myThread.start();
    //start方法的作用:启动一个分支线程 在JVM中开辟一个新的栈空间 这个代码完成后瞬间就执行结束了
    //新启动的线程会自动调用run方法 run方法在分支栈(分支线程)的最底部  main方法在主栈(主线程)的最底部
    // 因此 run 和main 是平级的
    for(int i=0;i<=1000;i++){
        System.out.println("主线程-->"+i);
    }
}

}
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<=1000;i++){
System.out.println(“分支线程–>”+i);
}
}
}
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
第二种 先创建一个类 实现Runnable接口 在进行重写run方法 然后再调用的时候 先创建一个实现了Runnable的类的对象
然后再创建Thread线程类 将刚刚创建了的实现了Runnable的类的对象 传入其中 即可
public class step02 {
public static void main(String[] args) {
Thread myt=new Thread(new MyThreads()); // Thread 是一个线程类 但是创建对象的时候 要传入一个Runnable对象
myt.start();
for(int i=0;i<=1000;i++){
System.out.println(“主线程–>”+i);
}
}
}
class MyThreads implements Runnable{ //这个继承的接口之后 并不是成为了线程类 而是一个可运行类
@Override
public void run() {
for(int i=0;i<=1000;i++){
System.out.println(“分支线程–>”+i);
}
}
}
----这是第二种 用匿名内部类是实现的 代码如下:
Thread niming=new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<=1000;i++){
System.out.println(“分支线程–>”+i);
}
}
});

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
第三种(jdk8的新特性)这种方法可以获得线程返回值 前面两种都没有返回值
public static void main(String[] args) {
FutureTask fk=new FutureTask(new Callable() { //第三种方法就是要实现Callable接口
@Override //这个是使用的匿名内部类来实现的第三种多线程
public Object call() throws Exception { //call就相当于run方法 当然也可以不使用匿名内部类 但是尽量还是使用匿名内部类 如果不想使用可以上网搜索
System.out.println(“第三种线程实现开始”);
Thread.sleep(1000);
System.out.println(“结束”);
int i=10;
int o=20;
return i+o;
}
});
Thread thread=new Thread(fk);
thread.start();
try {
Object sum=fk.get(); //这个地方来获取fk线程执行结果时 是main线程来获取fk线程的结果 因为此处的fk执行期间休眠一秒,所以main线程一定比fk线程快
// 所以在获取的时候 main线程会等(这样可能和调用普通方法差不多) 但是如果main线程有好多东西要执行 那么fk执行的快 会先得出结果
// 来等待main线程来调用 只要当main线程执行到此行代码的时候 才能让main线程得到结果 因此要比调用普通方法效率要高
System.out.println(“执行结果为———》”+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

}

}


获取线程的名字 .getName()
设置线程名字 .setName(“名字”)
线程默认名字为 Thread-0
Thread-1
Thread-N

currentThread()和sleep()都是静态方法
Thread cccc=Thread.currentThread();//获取当前线程对象 因为该代码出现在什么方法中 就获取什么线程对象
如 :该代码出现在main方法中 所以获取的是主线程 System.out.println(cccc.getName()) 输出的就是main
Thread.sleep(毫秒数);该代码出现在那 哪里就进行休眠
cccc.interrupt();//强行终止正在睡眠的线程 然后异常结束 (这个不是静态方法)
cccc.stop()😕/强行终止线程(已过时)缺点 :容易丢失数据 因为是直接强行终止的 数据容易丢(不是静态)
合理的终止线程 代码如下

package 多线程;

public class 合理的终止 {
    public static void main(String[] args) {
        bn mk=new bn();
       Thread lp=new Thread(mk);
       lp.start();
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mk.nu=false;
    }
}
class bn implements Runnable{
    boolean nu=true;
    @Override
    public void run(){
        for(int i=0;i<=100;i++) {
          if(nu==true){
            try {
               Thread.sleep(1000);
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            System.out.println("分支---->"+i);
          }else{
              //在这线程终止前 想要达到的目的 可以把代码写在此处
              return;
          }
      }
    }
}

===================================================
关于线程的调度(了解)
常见的线程调度模式:
抢占式:就是那个线程的优先级高 抢到的cpu时间片(就是cpu使用时间)就多一些概率,java就是该类型的
均分式:大家都一样 占用的cpu时间片长度相同
实例方法
void getPriority() 获取线程优先级
void setPriority(int newPriority) 设置线程优先级 其中的int newPriority是 填如 1–10 即可
最低优先级是 1 默认是5 最高是 10
优先级比较高的 在获取cpu时间片时可能会多一些(但也不完全是 只是提高了概率)
join() 如果mian线程中创建一个新的线程对象 st来执行其他东西 如果st.join() 就会先堵塞(停止)main线程
等到st所对应的线程执行完毕后 才继续执行main线程
静态方法
static void yield() 让位方法
暂停当前正在执行的线程对象 ,并执行其他线程
只是让该线程 先回到就绪状态 过会还有可能抢到执行权
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
线程安全------非常重要-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
什么时候触发多线程安全?
1.多线程并发
2.有共享的数据
3.共享数据发生修改的时候
满足上面三个条件的时候 就会发生
怎么解决线程安全问题?
线程排队机制(不能并发)称为"线程同步机制";就是牺牲效率来保证安全

异步编程模型:就是各自执行各自的,就是多线程并发(效率高)
同步编程模型:就是只能一次执行一个 不能同时执行 ,线程排队执行

+++++++++++++++synchronized()+++++++第一种用法+++++++
synchronized() 线程安全的关键,括号内的数据极为关键 如果你想要a1 a2 a3线程排队同步编程 里面就填什么
就比如IDEA thread取款代码在bank类中 可以参照
public void qvkuan(int qv){
synchronized (this){ //此处加入this 就相当于所有要访问这里的线程都要排队 也可以换成成员变量 或者是字符串常量池 总之要排队的 就要写这几个线程共享的变量
//先获取剩余的钱
int qw = this.yue;
//取钱
int er = qw - qv;
//更新余额
setYue(er);
}
}
对synchronized()的理解:
在Java语言中 任何对象都有“一把锁”,这个所就是一个标记
所以当有线程 t1 t2 遇到这个synchronized(共享数据(就是对象))后 总有一个先后原则,所以会有一个线程找到这个共享对象的锁
直到该线程执行完该代码块 才会不占用这个锁 其他线程才会执行
/*
Java三大变量:
实例变量:在堆中
静态变量:在方法区
局部变量:在栈中
局部变量不会有线程安全区问题 因为局部变量是不共享的
静态变量和实例变量 分别在方法区 和 堆中 方法区和堆都只有一个 可能会出现线程安全
实例变量和静态变量一起称为”成员变量“ 常量没有线程安全问题
*/
同步代码块越小 效率越高synchronized(){ } 所以大括号内地代码越少越好

+++++++++++++++synchronized()+++++++第二种用法+++++++
synchronized出现在实例方法上 这种方法会使整个方法都变得同步 会导致程序执行效率变慢 所以这种方法不常用
除非 是这个方法都是需要进行同步的 这样想 反而会有优势
ArrayList 非线程安全的
Vector 线程安全的
HashMap Hashset 是非线程安全
Hashtable 线程安全
那个锁(也就是标记) 只能是默认this这种方法不灵活
如:
public synchronize void qvkuan(int qv){
//先获取剩余的钱
int qw = this.yue;
//取钱
int er = qw - qv;
//更新余额
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setYue(er);
}

+++++++++++++++synchronized()+++++++第三种用法+++++++
用在静态变量上 类锁无论创建几个 类锁就一个 就是其他类中的静态方法 你创建的对象来调用方法 是在还是一个

关于锁的知识点可参考https://www.bilibili.com/video/BV1mE411x7Wt?p=320 310集-320集之间
synchronized()是锁中的排他锁

怎么解决线程安全问题?
1.尽量使用局部变量来代替实例变量和静态变量
2.如果必须使用实例变量 那么可以考虑创建多个对象 就能导致对象不共享
3.真没办法的时候 只能选择synchronized了

//
java线程分两大类
1.用户线程
2.守护线程
具有代表的是:垃圾回收器(守护线程)
main线程是用户线程
守护线程一般是死循环 所有用户线程只要结束 守护线程自动关闭
把线程变为守护线程 只需要cccc.setDaemon(true);即可

定时器
1.可以使用sleep方法先设置休眠时间 时间到了之后 在执行任务(这是最原始的定时器,有点low)
2.在java类库中 java.util.Timer中有已经准备好的一个定时器 可以拿来直接用
3.但是在实际开发过程中 使用较多的是Spring框架中提供的SpringTask框架

Object类中的wait和notify
这两个方法不是线程对象的方法 而是java对象中任意一个对象都有的方法
这个两个方法调用不是通过线程对象 不是cccc.wait(); 不是cccc.notify();《《《不是这两种方法》》》
建立一个Object对象(就是可以建立所有对象)
比如Object ob=new Object();
o.wait();
表示就是当在o对象上活动的线程进入等待状态(无限期等等待,直到被唤醒)
o.notify();
表示唤醒正在o对象上等待的线程
其中notifyAll();是唤醒在o对象上等待的所有线程;
生产消费模式wait notify要建立在synchronized的线程同步基础之上
wait会让正在o对象上活动的当前线程进入等待 并且占有o对象的释放锁
但是notify不会 他只会他发通知 不会释放之前占有对象的锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值