多线程编程

多线程编程
多线程编程
多线程编程

进程:一段程序的执行过程
1、动态性2、独立性3、并发性
并发:同一时刻,CPU交替运行多个任务
并行:同一时刻,CPU同时运行多个任务

线程存在于进程之中,二者之间主要区别在于:
(1)每个进程都需要操作系统为其分配独立的内存空间,
(2)而同一进程中的所有线程都在同一内存空间中工作,
这些线程可以共享同一块内存和系统资源。
线程编程相关API

Thread类
(1)继承Thread类线程实现的第一种方式:不被推荐
重写run(),这里面的代码,在线程被CPU选中后,需要执行!
.start()开始多线程
.setPriority(10);修改线程优先级(优先级默认是5,最低是1,最高是10)
就是指线程被CPU选中的概率

计算机程序是由CPU、程序代码和可存取的数据这三个要素组成的。
在Java中,多线程的机制是通过虚拟CPU来实现的。
(1)虚拟的CPU,封装在java.lang.Thread类中。
(2)虚拟CPU所执行的代码,传递给Thread类。
(3)虚拟CPU所处理的数据,传递给Thread类。

在Java中,虚拟CPU嵌入到Thread类中;构造一个线程时,
通过构造器参数传递执行代码和数据。一个线程可以执行相同或不同于其他线程的代码,
也可以访问相同或不同于其他线程的数据。
 public void start()。在一个单独的执行路径中启动线程,然后调用该Thread对象上的run()方法。
 public void run()。如果Thread对象是使用单独的Runnable目标实例化的,那么run()方法是被该Runnable对象调用的。如果我们编写一个类继承Thread,那么就是子类中被重写的run()方法被调用。
 public final void setName(String name)。改变Thread对象的名称,还有一个获取线程名的getName()方法 . Thread.currentThread().getName() 打印线程的名称
 public final void setPriority(int priority)。设置线程对象的优先级,取值范围为1到10。但是,开发者最好使用如下三个值:Thread.NORM_PRIORITY、Thread.MIN_PRIORITY和Thread.MAX_PRIORITY(取值分别是5、1和10)。
 public final void setDaemon(boolean on)。如果参数为true则表示该线程是一个守护线程。如果一个线程要成为一个守护线程,那么本方法就必须在线程启动前调用。
 public final void join(long millisec)。当前线程在第二个线程上调用本方法,将导致当前线程阻塞,直到第二个线程停止或运行了指定的微秒数。如果参数为0,那么第一个线程将无限期等待。
 public void interrupt()。中断本线程,如果因为某原因该线程被阻塞,那么这将导致本线程继续执行。
 public final boolean isAlive()。如果线程处于活动状态,则返回true。活动状态是指线程开始启动后,到在运行结束前的这一段时间的状态。
Runnable类
Runnable接口中为所有需要线程执行的任务定义了一个公共规范。如果我们查看JDK文档,我们会发现该接口只定义了一个方法run()。run()方法中执行的代码,就是我们想要让线程执行的代码。
Object类
Object类中包含了如下与多线程相关的方法:
(1)public final void wait()。导致当前线程在该对象上无限期等待,直到其它线程调用相同对象的notify()或notifyAll()方法通知它恢复执行为止。
(2)public final void wait(long timeout)。导致当前线程在该对象上等待,直到其它线程在相同对象上调用notify()或者notifyAll()方法,或者当指定的时间间隔过去,当前线程才会恢复运行。
(3)public final void notify()。唤醒正在该对象上等待的一个线程。当前线程必须拥有对象的锁以调用该方法。
(4)public final void notifyAll()。类似于notify(),但是是唤醒所有等待的线程。
线程的创建
在Java中,可以使用两种方法来创建一个新线程:
 编写一个继承Thread类的类,然后在类中重写Thread类的run()方法。
 编写一个类实现Runnable接口,然后将该类的实例与java.lang.Thread对象联系在一起。
通过继承Thread类来创建线程
通过继承Thread类来创建线程包括如下几个步骤:

  1. 创建一个继承Thread类的类。
  2. 在创建的Thread子类中重写run()方法,在方法中写入想要线程运行的代码。
  3. 创建Thread子类的实例。
  4. 通过调用该实例上的start()方法,开始运行线程。
    通过实现Runnable接口来创建线程
    用实现Runnable接口的方法创建线程的步骤包括:
     创建一个类实现Runnable接口,用于代表我们需要线程完成的任务。
     在Runnable指定的run()方法内,放入想要在线程中执行的代码。如果我们查看JDK文档,我们会发现Runnable接口中只定义了一个方法run()。因此,当我们用实现Runnable接口的方法创建线程时,就必须在Runnable接口的实现类中实现run()方法。run()方法中执行的代码,就是我们想要让线程执行的代码。当线程开始运行时,run()方法被调用;当run()方法运行结束时,线程就会终止。
     创建一个Runnable类的实例。
     创建一个Thread对象,将Runnable的实例做为构造器参数传入进去。要实际创建一个线程,需要将Runnable实例传递到Thread类的构造器中。例如:Thread(Runnable thrdObj, String thrdName),这里,thrdObj是实现Runnable接口的类的实例,thrdName指定线程的名称。
     通过调用Thread类的实例的start()方法,开始执行线程。调用start()方法将导致调用该线程关联的Runnable实例的run()方法。
    Runnable类的作用是将Thread对象与该对象要执行的任务分开。

两种创建方式的比较
 使用Runnable接口可以将虚拟CPU(Thread类)与线程要完成的任务有效分离,较好地体现了面向对象设计的基本原则。
 可以避免Java单继承的局限。在实际开发中,如果已经继承了某个类的子类要放入多线程中,由于Java中一个类不能拥有多个父类,所以就不能使用继承Thread类的方式,只能采用实现Runnable接口的方式。
基于以上原因,在实际Java项目开发中,有经验的程序员基本上都是用实现Runnable接口的方法创建线程。

使用yield()方法
使用yield()方法让线程停止运行。
如果调用yield()方法的线程的优先级太高的话,
该线程很可能会立刻由Runnable状态变为Running状态。

使用join()方法
线程一旦定义出来,默认是采用线程的异步调用
但是一旦在某一个线程中,对例外的线程执行的join():
将会使线程的异步调用,转换为同步调用,调用线程将会处于"等待"状态,直到被调用的线程,代码执行完毕之后,
调用线程才继续执行
如果join(10)方法中,传入参数,参数代码同步时间,
超过这个时间之后,同步将重新转换为异步
但是如果参数是0,代表调用线程,将会无限期等待被调用线程的执行
,直到被调用线程执行完毕
t.join(10); //join(0)等同于join();

线程池
• JDK5新增了一个Executors工厂类来生产线程池,有如下几个方法。
• public static ExecutorService newCachedThreadPool();
• public static ExecutorService newFixedThreadPool(int nThreads);
• public static ExecutorService newSingleThreadExecutor ();
• 这些方法的返回是ExecutorService对象,该对象表示一个线程池,可以执行Runable对象或者Callable对象代表的线程,
它提供了如下方法:
Future<?>submit(Runable task);
Futuresubmit(Callable task);

线程同步
(1) 有时两个或多个线程可能会试图同时访问一个资源例如,一个线程可能尝试从一个文中读取数据,而另一个线程则尝试在同一文件中修改数据
(2) 在此情况下,数据可能会变得不一致
(3) 为了确保在任何时间点一个共享的资源只被一个线,程使用,
使用了"同步"口使用同步关键字synchronized来进行标识

同步块、同步方法
(1) synchronized如果用于修饰对象的话, 除了可以修饰本对象之外,还可以修饰其他对象,
表示的含义是:
开启对象本身的对象锁,当多根线程同时访问该对象,
谁抢的了该对象锁,谁就可以访问同步块的代码!
(2)synchronized 修饰对象时, 对象也可以是自身, 也可以是其他对象
(3)synchronized也可以用来修饰成员方法,同样开启"自身对象”的对象锁

(4) synchronized修饰类对象时候,此时表示开启class对象的对象锁/当多根线程同时访问同步块时,谁抢的了该Class对象的对象锁,谁就可以访问同步块的代码

如何选择同步块,或者同步方法呢?
推荐大家选择同步块,原因是:
1、 方法的执行的时间,一定比某一段代码执行时间要长的多,
2、 同步方法开启的对象锁,只能是本对象的,那如果需要开启其他对象的呢?
3、
Synchronized (取得锁的对象){
//要锁定的代码
}
notify();//唤醒处于等待状态的线程
notifyAll();//唤醒所有处于等待状态的线程
多线程编程
多线程编程

多线程编程