Java笔记-多线程

多线程技术

区别                         进程                                                         线程

根本区别                 作为资源分配的单位                                调度和执行的单位

开销                  每个进程都有独立的代码和数据空间             线程可以看成是轻量级的

                        (进程上下文),进程间的切换会有               进程,同一类线程共享代

                          较大的开销                                                     码和数据空间,每个线程

                                                                                                有独立的运行栈和程序计

                                                                                                 数器(pc),线程切换的

                                                                                                 开销小。

所处环境        在操作系统中能同时运行多个任务                     在同一应用程序中有多个

                     (程序)                                                              顺序流同时执行

分配内存        系统在运行的时候会为每一个进程分                 除了CPU之外,不会为线程

                       配不同的内存区域                                              分配内存(线程所使用的资

                                                                                                  源是它所属的进程的资源)

                                                                                                  线程组只能共享资源

一个进程中包含了N多个线程,如果线程结束,进程并不一定结束。

进程结束,线程都将结束

CPU调度执行的是线程

通过继承Thread类实现多线程

实现多线程的步骤

  1. 继承Thread类
  2. 重写run()方法
  3. 通过start()方法启动线程

一定的缺点:Java中的类是单继承 的,一旦继承了Thread类,就不允许再去继承其他的类

package com.bjsxt.thread;

public class MyThread extends Thread {//(1)继承

             @Override

              public void run() {

                 /**线程体*/

                 System.out.println("MyThread-------------");

             }

}

package com.bjsxt.thread;

public class Test {

             public static void main(String[] args) {

                  //创建线程类的对象

                  MyThread my = new MyThread();

                  my.start();//启动线程

                   System.out.println("-----------main");

             }

}

两条线程谁先谁后不一定

package com.bjsxt.thread;

public class MyThread2 extends Thread{

         @Override

          public void run() {

                 for(int i = 0;i<=10;i++) {

                       System.out.println("MyThread2---------------"+i);

                }

          }

}

package com.bjsxt.thread;

public class Test2 {

           public static void main(String[] args) {//主线程

                      //创建线程类的对象

                      MyThread2 my=new MyThread2();

                      //启动线程

                       my.start();

                       //主线程中for循环

                       for(int i=0;i<=10;i++) {

                            System.out.println("-------main---------"+i);

                       }

              }

}

通过实现接口Runnable实现多线程

实现Runnable接口实现多线程的步骤

  1. 编写类实现Runnable接口
  2. 实现run()方法
  3. 通过Thread类的start()方法启动线程

静态代理模式

Thread ➡代理角色

MyRunnable ➡ 真实角色

代理角色与真实角色实现共同的接口Runnable接口

举例:

You 结婚 真实角色

MarraryCompary(代理角色)

共同拥有结婚的接口 结婚的方法

暂停线程执行sleep_yield_join_stop

暂停线程执行的方法

方法名                                                                              描述

final void join()                                                                  调用该方法的线程强制执行(主线程调用,其他线程加

                                                                                         入主线程阻塞), 其他线程处于阻塞状 态,该线程执

                                                                                         行完毕后, 其他线程再执行

static void sleep(long millis)                                             使用当前正在执行的线程休眠millis秒,线程处于阻塞状态

static void yield()                                                              当前正在执行的线程暂停一次 ,允许其他线程执行,

                                                                                         不阻塞,线程进入就绪状态,如果没有其他等待执行

                                                                                          的线程,这个时候当前线程就会马上恢复执行。

final void stop()                                                                 强迫线程停止执行。已过时。不推荐使用。

总结

sleep:

不会释放锁,sleep时别的线程也不可以访问锁定对象。

yield:

让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。(有可能跳回自己)

join:

当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行

获取线程基本信息的方法

线程操作的常用方法

方法名称                                                描述

static Thread currentThread()                返回目前正在执行的线程

final String getName()                            返回线程的名称

final boolean isAlive()                             判断线程是否处于活动状态

线程的优先级问题

设置和获取线程优先级的方法

方法名                                                   描述

final int getPriority()                               获取线程的优先级

final void setPriority(int priority)            设置线程的优先级

线程状态_线程的生命周期

线程状态

新生状态

       用 new关键字建立一个线程后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态。

就绪状态

       处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU。当系统选定一个等待执行的线程后,它就会从就绪装状态进入执行状态,该动作称为“CPU调度”。

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. 不知道什么时候会被选中执行
  2. 遇到等待IO输入或其他耗时的操作,由运行状态进入阻塞状态
  3. 导致阻塞的事件结果,就绪状态,等待CPU选中执行
  4. 运行状态时,给的时间片内没有执行完run方法中的代码,进入就绪状态,下一次被CPU选中时,从上次“断”掉的地方,继续执行。

线程同步_具体实现

同步实现的方式

同步代码

synchronized(obj){

       //中的obj称为同步监视器

}

同步方法

同步方法的同步监视器为当前对象this

public synchronized....方法名(参数列表){

}

同步监视器只能是对象,推荐使用资源共享的对象,可以是当前对象this,也可以是其他的对象

死锁的避免

银行家算法:

该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。(计算资源的大小,计算出来后,永远按照从大到小的方式来获得锁,大的先得)

生产者消费模式的实现_1

生产者与消费者的原理

多线程 : 数据错乱,不同步

加锁:数据不错乱,但生产与消费不同步

解决:

线程间通信的方法

wait():调用了wait()方法的线程进入等待池进行等待,等待池中的线程不会去竞争对象锁,直到其它的线程通知,才会进入锁池

wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。

         当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。

       只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

          也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程

wait() 需要被try catch包围,中断也可以使wait等待的线程唤醒。

notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。

notify():随机唤醒一个在该对象上等待的线程,被唤醒的线程进入锁池,开始竞争该对象锁

notifyAll(): 唤醒所有在该对象上等待的线程,优先级高的线程可能先竞争到对象锁,只能在同步方法和同步代码块中使用

notify 和 notifyAll的区别

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。

注意条件判断,唤醒后接着wait()后运行,if不可加else会跳出(即执行if外的代码),不如用while

猜你喜欢

转载自blog.csdn.net/qq_39655510/article/details/81464806