java多线程学习知识点笔记1-多线程基础,lock接口,写时复制

java多线程(juc)


一.基本知识

1.1什么是juc
java.util.concurrent 包的缩写  
1.2.进程与线程

cpu个数、核数、线程数的关系?

cpu个数:是指物理上,也及硬件上的核心数;

核数:是逻辑上的,简单理解为逻辑上模拟出的核心数;

线程数:是同一时刻设备能并行执行的程序个数,线程数=cpu个数 * 核数
1.3.什么是进程?
    是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
1.4什么是线程?
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1.5java中线程的几个状态?
New(新建状态),
RUNNABLE(准备就绪,正在运行中),
BLOCKED,(等待另一个线程释放锁)
WAITING,(等待)
TIMED_WAITING,(过时不侯)
TERMANATED,(终结)

线程状态的切换:

在这里插入图片描述

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
    (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1.6wait(),sleep(),yield()的区别?

1.6.1 sleep():让线程睡眠
Thread类中的方法
sleep()方法是Thread类的静态方法,调用线程会暂时让出指定时间的CPU执行权,把CPU执行权让给其他线程,等到睡眠时间一到,该函数就会正常返回,此线程会自动苏醒。苏醒后,线程就处于就绪状态,然后参与CPU的调度,获取到CPU资源后就可以继续运行了。

注意:sleep方法会让出CPU资源,但是不会释放锁.

如果在睡眠期间其他线程调用了该线程的interrupt()方法中断了该线程,则该线程会在调用sleep方法的地方抛出InterruptedException异常而返回。

1.6.2 wait():释放锁进入阻塞状态
Object类中的方法
当前线程进入阻塞状态,释放锁,在使用notify() 或 notifyAll()方法后在再次进入准备状态,继续在wait()方法后执行
需要注意的是:被唤醒并不代表立刻获得对象的锁,要等待执行notify()方法的线程执行完,即退出synchronized代码块后,当前线程才会释放锁,而呈wait状态的线程才可以获取该对象锁。

而且被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行。

如果调用wait()或者nofity()方法时没有事先获取该对象的监视器锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch语句进行捕获异常。

notify方法只会(随机)唤醒一个正在等待的线程,而notifyAll方法会唤醒所有正在等待的线程。如果一个对象之前没有调用wait方法,那么调用notify方法是没有任何影响的。

1.6.3 yield():让出CPU执行权
Thread类中的方法
yield()方法是Thread的静态方法,它的作用是放弃当前的CPU资源,将它让给其他优先级更高的线程去占用CPU执行时间。但放弃时间不确定,有可能刚刚放弃,马上又获得CPU时间片。这里需要注意的是yield()方法和sleep方法一样,线程并不会让出锁,和wait方法不同

sleep与yield方法的区别在于,当线程调用sleep方法时调用线程会被阻塞挂起指定的时间,在这期间线程调度器不会去调度该线程.而调用yield方法时,线程只是让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪状态,线程调度器下一次调度时就有可能调度到当前线程执行 。

下面是在其他网站摘抄的使用对象

栈长没用过 yield,感觉没什么鸟用。
如果一定要用它的话,一句话解释就是:yield 方法可以很好的控制多线程,如执行某项复杂的任务时,如果担心占用资源过多,可以在完成某个重要的工作后使用 yield 方法让掉当前 CPU 的调度权,等下次获取到再继续执行,这样不但能完成自己的重要工作,也能给其他线程一些运行的机会,避免一个线程长时间占有 CPU 资源。

1.6.4 join():等待线程执行终止
Thread类中的方法
在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

如果在一个线程A中执行了thread.join()语句,其含义是:当前线程A必须等待thread线程终止之后才从thread.join()返回。

1.6.5 interrupt():
不要以为它是中断某个线程!它只是线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么 这个线程还是不会中断的!

1.7 线程优先级

线程的调度

1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:

  1. static int MAX_PRIORITY
    线程可以具有的最高优先级,取值为10。
  2. static int MIN_PRIORITY
    线程可以具有的最低优先级,取值为1。
  3. static int NORM_PRIORITY
    分配给线程的默认优先级,取值为5。

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。


二.Lock接口

1.synchronized复习

多线程编程模版
创建资源类
在资源类中创建同步方法同步代码块

2.Lock是什么
Lock接口的实现提供了比同步代码块和同步方法更好的更广泛的锁操作,它允许更灵活的接口和更广泛的锁操作,并支持多个关联操作对象
3.Lock的使用
Lock lock = new ReentrantLock();

lock.lock()try{
	method body
}finally{ 
	lock.unlock()}

4.创建多线程的方式
继承Thread类 ----new Thread().start()
实现Runnable接口 ---- new Thread(new Runnable(){ 重写run方法 }).start();
实现Callable接口 ----FutureTask ft = new FutureTask(new Callable(){ 重写call() }); ft.get()
线程池

5.线程间通信
判断–>干活–>通知

使用synchronized实现多个线程交替加减某一个值

//分别起两个对number进行交替加减1的操作
class ThreadDemo{
	int number = 0;
	
	public synchronized void incr()throws Exception{
	//使用while循环判断number的值 避免使用if多个线程操作的时候 某个线程在等待后抢到资源直接执行下方语句,线程wait后应再次判断是否符合条件在执行(多线程之间的虚假唤醒)
		while(number != 0){
			this.wait();
		}
		number++;
		System.out.println(number);
		this.notifyAll();
	}
	public synchronized void decr()throws Exception{
		while(number != 1){
			this.wait();
		}
		number--;
		System.out.println(number);
		this.notifyAll();
	}
}

使用Lock实现多个线程交替加减某一个值

public class ThreadTest {
	int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();

    public void incr(){
        lock.lock();
        try {
            while (number != 0){
                condition1.await();
            }
            number++;
            System.out.println(number);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            condition2.signal();
        }
    }

    public void decr(){
        lock.lock();
        try {
            while (number != 1){
                condition2.await();
            }
            number--;
            System.out.println(number);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            condition1.signal();
        }
    }
	@Test
    public void test1(){
        User u = new User();
        new Thread(()->{
            for (int i=0; i<10; i++) {
                System.out.println(Thread.currentThread().getName());
                u.incr();
            }
        },"thread1").start();

        new Thread(()->{
            for (int i=0; i<10; i++) {
                System.out.println(Thread.currentThread().getName());
                u.decr();
            }
        },"thread2").start();
    }
}

6.list集合线程不安全
多线程操作list集合是时报java.util.ConcurrentModificationException 并发修改异常
在这里插入图片描述
解决方案:
使用Vector()
或者List list = Collections.synchronizedList(new ArrayList());
但是上面两种解决方案的list集合中的方法都为 synchronized的方法是串行的 无法并行操作list集合

优化方案:
使用 CopyOnWriteArrayList();
CopyOnWriteArrayList();使用写时复制技术
在这里插入图片描述
对数组中的元素进行操作时 会加锁 复制一个新的集合进行操作 操作完毕会替换原有的集合
当查询集合中的元素的时候 不会加锁 直接返回;
这样保证了查询不加锁 修改加锁 可能数据实时性稍差 但是性能增加了。

Set的同步集合 CopyOnWriteArraySet() map的同步集合 ConcurrentHashMap()

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值