JAVA多线程笔记

来源:互联网 发布:资金流水表设计 mysql 编辑:程序博客网 时间:2024/05/27 14:12


线程与进程:

一个程序至少有一个进程,一个进程至少有一个线程.  

 

进程

是一个正在执行的程序,每个进程执行都有一个执行顺序,也叫执行路径,或者控制单元。

进程在执行过程中拥有独立的内存单元

线程

是一个进程中的一个控制单元,线程控制着进程的执行

多个线程共享内存,极大地提高了运行效率

 

在windows环境下JVM启动的时候会有一java.exe进程。该进程至少有一线程负责java程序的执行,该线程的代码存在于main方法中,main方法作为java程序初试线程的起点,其他的任何线程都是由这个初始线程启动的。

 

JVM有两种线程:守护线程和非守护线程,守护线程是有JVM自己使用的,比如垃圾回收机制。Java的初试线程是非守护线程。

 

多线程的意义在于,在一个应用程序中,可以有多个部分同时执行,而操作系统并没有将多个线程看做独立的应用,以实现进程的调度、管理和资源的分配。

 

线程的创建:

1)  方法一:定义类继承java.lang.Thread,复写 run() 方法,通过实例化对象调用 start() 方法。

public void start()

l  使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

l  结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。

l  多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

代码示例:

class ThreadDemo extends Thread

{

    public void run() 

    {

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

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

        }

    }

}

public class TestThread

{

    public static void main(String [] args) {

        ThreadDemo td = new ThreadDemo();

        td.start();

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

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

        }

    }

}



 

2)  方法二:

声明实现 java.lang.Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递给一个新的Thread对象,然后通过start()方法启动一个线程。

public Thread(Runnable target)

分配新的 Thread 对象。这种构造方法与 Thread(null, target,gname) 具有相同的作用,其中的 gname 是一个新生成的名称。自动生成的名称的形式为 “Thread-”+n,其中的 n 为整数。

代码示例:

class ThreadDemo implements Runnable

{

    public void run() {

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

            System.out.println(Thread.currentThread()+"running");

        }

    }

}

public class Test

{

    public static void main(String [] args) {

        new Thread(new ThreadDemo()).start();

    }

}

两种线程的创建方式的对比:

方法一:继承Thread类

只能单继承Thread

存放在Thread子类的run()方法中

方法二:实现Runable接口

避免了单继承的局限性

存放在实现Runable接口类的run()方法中

 

线程的状态:

 

线程安全问题:

问题原因:

当多条语句在操作一个线程共享数据时,其中一个线程只对多条数据执行了一部分,还未执行完,另一线程启动,参与到共享数据中来,就会造成共享数据的错误。

解决办法:

操作线程,将操作共享数据的多条语句,在一个线程中全部执行完,执行过程中,其他线程不能参与。

Java提供了“同步代码块”来解决线程安全问题。

Synchronized (对象) {

   //多条语句

}

语句中的对象如同锁,持有锁的线程可以再同步中执行,没有取得锁的线程即使获得cpu执行权,也无法执行同步代码。

同步方法

public void synchronized run() {

 

}

静态方法与成员方法的对象锁:

函数需要被对象调用,那么函数都有一个所属对象的引用,同步函数的锁是this。

同步函数被静态修饰时,使用的线程锁是该类的字节码文件对象,类.class

线程安全前提:

至少两个以上线程。

是否用的同一个线程。

单例模式:懒汉式

class Single

{

    private static Single s = null;

    private Single() {}

    public static Single getInstance() {

        if(s==null) {

            synchronized(Single.class) {

                if(s==null) {

                    s = new Single();

                }

            }

        }

        return s;

    }

}

 

线程间通信:

多个线程操作同一资源,但是操作的动作不同。

线程死锁问题:

两个线程同时使用相同的两个线程锁,但是每一个线程只拥有其中的一个线程锁,两个线程不放开自己的线程锁,于是便产生了线程死锁的问题。

等待唤醒机制:都使用在线程同步中,因为都要对持有对象锁的那个线程操作

publicfinal void wait() throwsInterruptedException

导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。 当前的线程必须拥有此对象锁。

public final void notify()

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

易错点: 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。并不是使用notify();之后就执行新的线程,只有在当前线程放弃当前线程锁的时候,才随即选择等待在当前对象上的线程。

 

JDK1.5新特性

将同步关键字synchronized替换成现实Lock操作。Lock 替代了synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

注意:一定要进行线程解锁。

void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。

假定调用此方法时,当前线程保持了与此 Condition 有关联的锁定。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常,将抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。

void signal() 唤醒一个等待线程。

如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁定。

void signalAll()  唤醒所有等待线程。

如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁定。

 

javaAPI示例:

 class BoundedBuffer {

   final Lock lock = new ReentrantLock();

   final Condition notFull  = lock.newCondition();

   final Condition notEmpty = lock.newCondition();

   final Object[] items = new Object[100];

   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {

     lock.lock();

     try {

       while (count == items.length)

         notFull.await();

       items[putptr] = x;

       if (++putptr == items.length) putptr = 0;

       ++count;

       notEmpty.signal();

     } finally {

       lock.unlock();

     }

   }

   public Object take() throws InterruptedException {

     lock.lock();

     try {

       while (count == 0)

         notEmpty.await();

       Object x = items[takeptr];

       if (++takeptr == items.length) takeptr = 0;

       --count;

       notFull.signal();

       return x;

     } finally {

       lock.unlock();

     }

   }

 }

 

 

守卫线程:

守卫线程是JVM的后台线程,例如垃圾回收机制,随着JVM的线程启动而启动,JVM的所有非守卫线程结束而结束。

public final void setDaemon(boolean on)

将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。

该方法必须在启动线程前调用。

该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。

on - 如果为 true,则将该线程标记为守护线程。

 

 

线程的常用方法:

public final int getPriority()  返回线程的优先级。

public final void setPriority(int newPriority) 

更改线程的优先级。

首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。

在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。

优先级:public static final int MIN_PRIORITY/NORM_PRIORITY/MAX_PRIORITY

public void interrupt()  中断线程。

public static void yield()  暂停当前正在执行的线程对象,并执行其他线程。

 

使用Executor(扩展)

通过使用java.util.concurrent包中的执行器(Executor)管理Thread对象,简化多线程编程。

Executor 在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Excutor允许你管理异步任务的执行,而无须显示的管理线程的生命周期。

Excutor是启动任务的优选方法。

java.util.concurrent 下 接口 Executor

执行已提交的 Runnable 任务的对象。

此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。

例如,可能会使用以下方法,而不是为一组任务中的每个任务调用 new Thread(new(RunnableTask())).start():

 Executor executor = anExecutor;

 executor.execute(new RunnableTask1());

 executor.execute(new RunnableTask2());

 ...

构造方法

void execute(Runnable command)

在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。

 

ExecutorService是具有生命周期的Executor。

代码示例

Import java.util.cincurrent.*;

class Thre implements Runnable

{

    public void run() {}

}

class ThreadTest

{

    public static void main(String[] args)

    {

        ExecutorService exec = Exectors.newCachedThreadPool();

;

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

            exec.execute(new Thre());

            exec.shutdown();

        }

    }

}

 

java.util.concurrent

类 Executors:

此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。此类支持以下各种方法:

创建并返回设置有常用配置字符串的ExecutorService 的方法。

创建并返回设置有常用配置字符串的ScheduledExecutorService 的方法。

创建并返回“包装的”ExecutorService方法,它通过使特定于实现的方法不可访问来禁用重新配置。

创建并返回 ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。

创建并返回非闭包形式的 Callable的方法,这样可将其用于需要 Callable 的执行方法中


原创粉丝点击