java多线程

来源:互联网 发布:微博来自mac客户端 编辑:程序博客网 时间:2024/05/01 19:42
Java多线程 : 
  线程跟进程的区别:
    1.线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
2.进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。
       多进程是指操作系统能同时运行多个任务(程序)。
       多线程是指在同一程序中有多个顺序流在执行。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

多线程的实现:
在Java 1.4及以下的版本中,每个线程都具有新建、可运行、阻塞、死亡四种状态
在Java 5.0及以上版本中,线程的状态被扩充为新建、可运行、阻塞、等待、定时等待、死亡六种。线程的状态完全包含了一个线程从新建到运行,最后到结束的整个生 命周期

1. NEW(新建状态、初始化状态):线程对象已经被创建,但是还没有被启动时的状态。这段时间就是在我们调用new命令之后,调用start()方法之前。
    2. RUNNABLE(可运行状态、就绪状态):在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。
    3. BLOCKED(阻塞状态、被中断运行):线程正在等待其它的线程释放同步锁,以进入一个同步块或者同步方法继续运行;或者它已经进入了某个同步块或同步 方法,在运行的过程中它调用了某个对象继承自java.lang.Object的wait()方法,正在等待重新返回这个同步块或同步方法。
    4. WAITING(等待状态):当前线程调用了java.lang.Object.wait()、 java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三个 中的任意一个方法,正在等待另外一个线程执行某个操作。比如一个线程调用了某个对象的wait()方法,正在等待其它线程调用这个对象的notify() 或者notifyAll()(这两个方法同样是继承自Object类)方法来唤醒它;或者一个线程调用了另一个线程的join()(这个方法属于 Thread类)方法,正在等待这个方法运行结束。
    5. TIMED_WAITING(定时等待状态):当前线程调用了 java.lang.Object.wait(long timeout)、java.lang.Thread.join(long millis)、java.util.concurrent.locks.LockSupport.packNanos(long nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个方法中的任意一个,进入等待状态,但是与WAITING状态不同的是,它有一个最大等待时间,即使等待的条件仍然没有满足,只要到 了这个时间它就会自动醒来。
    6. TERMINATED(死亡状态、终止状态):线程完成执行后的状态。线程执行完run()方法中的全部代码,从该方法中退出,进入TERMINATED状态。还有一种情况是run()在运行过程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入TERMINATED状态。


第一种方式  是直接调用 继承的类
public class ThreadDemo extends Thread{


public static void main(String[] args) {
Thread th_A = new Thread(new Runnable() {
public void run() {
sl("th_A");
}
});

Thread th_B = new Thread(new Runnable() {
public void run() {
sl("th_B");
}
});

th_A.start();
th_B.start();
}

public static void sl( String  name){ 
for (int i = 0; i < 10; i++) {
System.out.println("name ->" + name + "--->i" + i);
}
}
}


第二种方式 实现Runnable接口
public class RunnableDemo implements Runnable{

public void run(){
for(int i = 0; i < 10; i ++){
if(i == 5){
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(i);
}
}


public static void main(String[] args) {
RunnableDemo nable = new RunnableDemo(); 
Thread thread1 = new Thread(nable);
Thread thread2 = new Thread(nable);
Thread thread3 = new Thread(nable);
//因为开启的三个线程都是用的同一个runnable对象,所以就实现了资源共享
//所以开发中 大多以实现Runnable接口来 实现多线程
}
}


Thread, Runnable 两种实现方式的区别和联系:
   在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比 继承Thread类有如下好处:
   ->避免点继承的局限,一个类可以继承多个接口。
   ->适合于资源的共享










/*
1)start方法
         start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
  2)run方法
   run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
  3)sleep方法
          sleep方法有两个重载版本:
sleep(long millis)     //参数为毫秒
sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒
sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
     但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象
  4) yield方法
         调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,
另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
        注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
   5)join方法
       join方法有三个重载版本:
join()
join(long millis)     //参数为毫秒
join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒
假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,
则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的事件。
wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。
   由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁
6)interrupt方法
     interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;
          另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。
7)stop方法
   stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。
   8)destroy方法
  destroy方法也是废弃的方法。基本不会被使用到。
  以下是关系到线程属性的几个方法:
  1)getId
   用来得到线程ID
  2)getName和setName
   用来得到或者设置线程名称。
  3)getPriority和setPriority
   用来获取和设置线程优先级。
  4)setDaemon和isDaemon
  用来设置线程是否成为守护线程和判断线程是否是守护线程。
  守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
  Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。















volatile , synchronized wait、notify和notifyAll








1.synchronized关键字不能继承。
    虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。
    如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地 在子类的这个方法中加上synchronized关键字才可以。
    当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类 调用了父类的同步方法,因此,子类的方法也就相当于同步了
    2.在定义接口方法时不能使用synchronized关键字。
    3.构造方法不能使用synchronized关键字,但可以使用下节要讨论的synchronized块来进行同步。
    4.synchronized可以自由放置。
    5.synchronized 自由放置
         public synchronized void method();
    synchronized public void method();
public static synchronized void method();
public synchronized static void method();
synchronized public static void method();
    6.但要注意,synchronized不能放在方法返回类型的后面
    7.synchronized关键字只能用来同步方法,不能用来同步类变量.
    8.虽然使用synchronized关键字同步方法是最安全的同步方式,但大量使用synchronized关键字会造成不必要的资源消耗以及性能损失 
    9.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。
 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
 
 
 
 
 
 
 
 
 
 
脏数据:
 当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,
那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
   
0 0
原创粉丝点击