Java多线程

参考文章(https://baijiahao.baidu.com/s?id=1685762284067131135&wfr=spider&for=pc)

java多线程:

线程类都要实现runable或者callable接口 

runable:无返回值

callable:有返回值

Executors能创建四种线程池:
Executors.newCachedThreadPool:可变长度线程池,线程可无限增加(oom)
Executors.newFixedThreadPool: 定长线程池,线程池大小可限定,超过限定不再新增线程,队列是无堵塞队列(无限增加线程任务会oom)
Executors.newSingleThreadExecutor: 单例线程池,单线程依次执行线程任务,队列是无堵塞队列(无限增加线程任务会oom)。
Executors.newScheduledThreadPool:定时任务线程池,线程可无限增加(oom)
ThreadPoolExecutor pool1 = new ThreadPoolExecutor()(推荐线程池创建方式,但是得自己设置参数,比如核心线程数,最大线程数,空闲线程超时时间等)

关键:
synchronized:锁
分为对象锁和类锁。
对象锁:指锁住某个对象,有多个线程调用该对象时,需要依次调用,不能并发调用,因为锁一次只能有一个线程获取。
类锁:指整个类都被锁住了,多个线程调用该类时,只能依次调用。(仅用于静态方法)举例:看截图

也可以不使用同步锁关键字 直接使用java封装的互斥锁 Lock lock = new ReentrantLock();
加锁 lock.lock();
释放锁: lock.unlock();
让线程进入wait : lock.wait()
唤醒线程:lock.notify() lock.notifyall();

synchronized有锁升级的过程
无锁:最开始对象没有锁住线程的状态
偏向锁:线程将线程ID给予锁对象的对象头中,下次线程来就直接锁住。
自旋锁:多个线程互相竞争锁。
重量锁:多个对象互相竞争超过一定数量和一段时间后升级为重量级锁。
锁降级只发生在GC过程中。
每一次锁的资源都是向CPU申请的,而CPU锁的资源是有限的,如果申请完了就会进入申请等待的过程,很耗时间。
ReentrantLock分为公平锁和非公平锁
new ReentrantLock(true)公平锁:线程竞争依次竞争
new ReentrantLock(false)非公平锁:出现插队行为
对象在堆中的对象头

 



Class TestCallAble implements CallAble{

    run(){
    synchronized(this) {
      sout("test");
  }
}
}

main(){
//像这种情况 锁就没用 因为创建了线程任务对象a1 线程任务对象a2 且分配两个线程分别执行a1,a2任务 不会有两个同时执行一个任务
//除非多个线程执行同一个线程任务才会去争锁类似:pool.submit(a1);pool.submit(a1);两个线程执行同一个任务
//对象锁也是两个 a1,a2都分别有自己的对象锁
  TestCallAble a1 = new TestCallAble ();
   TestCallAble a2 = new TestCallAble ();
  pool.submit(a1);
  pool.submit(a2);

}

//换个方式:将锁放在静态方法上就变成了类锁,虽然同样是两个线程任务对象,但是由于类的静态方法是在对象创建前加载的,因此该类的所有实例调用静态方法都要争锁。

 

 

 现在拿个例子来详细说明:

让两个线程轮流打印0~100之间的数字。

AtomicInteger:线程可见性,每次更改所有线程可见。

见代码 由于是新建了两个任务对象,且锁是加在lock对象上的,如果不从外部传入一个共用对象,则锁就是加在了两个任务对象实例各自的lock属性对象上,线程只会执行各自的线程任务,无法去争同一个线程任务的锁。

同样的由于原子变量也要从外部传入,不然也是各自任务对象的原子变量;(举例:当第一个线程进入任务线程1的 run()方法,原子变量是内部初始化的1,第二个线程进入任务线程2的 run()方法,原子变量也是任务线程2的对象属性,也是初始化的1,

但是通过外部引入,则线程任务1和线程任务2的原子变量的引入地址就是同一个原子变量(外部的原子变量flag的地址))lock对象同理,因为两个线程任务对象的lock指向同一个lock对象(外部对象),因此两个线程执行各自线程任务对象的run方法时,则需要去争锁,因为lock对象被锁住了,一次只能有一个线程拿到锁。

介绍完上面:讲代码执行流程:

新建两个线程任务,然后线程池分配两个线程分别执行任务1和任务2,假设线程1先执行(因为线程执行无序),进入 run()方法,执行代码,发现lock对象有锁,于是率先抢到锁,线程2同时也进入了线程任务2的run(),但是发现锁已经被线程1拿到了,线程2只能等待,线程1发现flag==1因此打印且将flag 设置为2,然后调用notify ()方法,唤醒该对象锁wait的一个线程(如果要唤醒多个用notifyall()),然后调用wait()方法,让执行该代码的线程(线程1执行该代码)进入 wait状态且释放锁。

然后锁被线程2拿到,线程2由于flag==2,因此执行打印,然后调用notify()将其他线程进入唤醒状态(线程1是wait状态变成唤醒状态),然后调用wait()方法,线程2进入wait状态然后释放锁。

然后锁被线程1拿到。。。。循环上述步骤。即实现了线程1和线程2轮流打印。

 

 

 如果要实现

线程1打印0

线程2打印1

线程1打印2

线程2打印3

。。。。

将上述循环改成50次,然后再新增一个原子变量,每次++,然后打印即可。


 

 

 

posted on 2024-06-26 02:29  丶柚子  阅读(3)  评论(0编辑  收藏  举报

导航