java thread

进程是线程的容器,一个进程中至少有一个线程,线程不能脱离进程而单独存在

线程,有时被称为轻量级进程(LightweightProcess,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,那就是程序本身。

线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

操作系统对进程的定义是特定的代码序列在指定数据集合上的一次执行活动,可见这是一个动态概念,所以一些资料说进程就是可执行文件是不正确的。操作系统对进程的定义有些晦涩,通俗点讲,进程就是正在计算机上运行的可执行文件针对特定的输入数据的一个实例,同一个可执行程序文件如果操作不同的输入数据就是两个不同的进程,理解这一点是很重要的,当你看到系统中有多个相同名字的进程同时存在时就不会再感到困惑。象Windows这样的多任务分时操作系统对进程的调度是针对线程进行的,也就是说线程是进程调度的基本单位(当然,支持超线程的CPU问世后这个概念需要重新定义)。

那么进程和线程到底是什么关系呢?其实很简单,可以把进程理解为一个线程容器,线程不能独立存在,它必须隶属于某个进程,而进程也至少拥有一个线程,如果一个进程的所有线程都结束了那么进程也就结束了。

 

完成一个任务可以有多进程和多线程两种方式,到底哪种方式快呢?通常人们认为Windows系统是以进程为单位分配CPU时间片的,那么使用多个进程就可以多分得一些CPU时间,自然就会比使用单独进程多个线程的方式快一点了。事实上这种情况并不绝对正确,首先,创建进程比创建线程要多占用系统资源,系统资源不足往往会引起系统性能的下降,导致任务完成的比较慢。其次,由于多个进程要操作同一个数据集合,必然会因为数据争用导致进程状态改变,同多个线程状态改变相比,进程切换要使用更多的CPU时间。最后,使用单进程方式,由于进程少,每个进程又可以较多的获得CPU时间片,从而能够很大的改善进程的性能。

 

synchronized功能性的限制 —— 它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁。同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行

 

public synchronized static funStatic()静态方法比较特殊 因为不存在有this当前对象这个说法 所以 他使用的锁对象是A.class 也就是 这个类的Class对象 他在JVM上由JVM保证是唯一一个 这样 锁定的效果就影响所有静态的同步方法

一个类有同步方法,还有非同步的方法,一个线程进入一个同步方法后,另外一个线程能访问其他非同步的方法,但不能访问其他同步的方法。

关键是看加锁的对象,如果一个对象被加锁了,那么请求这个对象的锁时,才需要申请锁。

 

Lock能完成synchronized所实现的所有功能Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

 

Java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。

 

Lock 的锁定是通过代码实现的,而synchronized 是在JVM 层面上实现的。

 

synchronized 在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是Lock 的话就享受不到JVM 带来自动的功能,出现异常时必须在finally 将锁释放掉,否则将会引起死锁。

 

在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。

 

ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

 

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态(ready,start, run, stop),那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。

suspend()方法容易发生死锁。调用suspend() 的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

通过suspend()使线程进入停滞状态后,除非收到resume()消息,否则该线程不会变回可执行状态。

 

Thread.yield() 这个方法是线程方法,当一个线程抢到执行权后,执行到yield()方法后,就会放弃执行权,其他线程就可以拿到执行权了.当调用yield()函数后,线程不会释放它的“锁标志”

Thread.yield()该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会

thread.yield() 调用,并不保证下一个运行的线程就一定不是该线程

yield()方法可以使正在运行的线程放弃当前分得的cpu时间,但是不会使线程阻塞,即线程仍处于可执行状态,随时可能再次分得cpu时间。调用yield()方法的效果相当于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

 

 

Thread.join() 阻塞 “调用线程”(主线程) 直到某个线程结束  Blocks the calling thread until a threadterminates

static void Main(){

     Thread t=newThread(new ThreadStart(ThreadMethod));

     t.Start();

     t.Join();

    Console.WriteLine("I am Main Thread");

    Console.Read();

}

从上面的代码中,我们可以看到存在两个线程:主线程和线程t

回到Join,这里所说的调用方就是主线程,主线程调用线程t的Join方法,导致主线程阻塞,直到t线程执行完毕,才返回到主线程中。

简单理解,在主线程中调用t.Join(),也就是在主线程中加入了t线程的代码,必须让t线程执行完毕之后,主线程(调用方)才能正常执行。

 

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

  (1)thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  (2)在Daemon线程中产生的新线程也是Daemon的。

  (3)守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

当正在运行的线程都是守护线程时,Java虚拟机退出.

在我们的系统中经常应用各种配置文件(黑名单,禁用词汇),当修改配置文件后,一般要重启服务,系统才能够加载;当重启服务的代价比较高的情况下,这种加载方式不能满足我们的要求,这个时候守护线程该发挥它的作用了,它可以实时加载你的配置文件,无需重启。

 

class Deadlocker { 

int field_1, int field_2; 

private Object lock_1 = newint[1]; 

private Object lock_2 = newint[1]; 

                publicvoid method1(int value) { 

                                synchronized(lock_1) { 

                                                synchronized(lock_2) { 

                                                                field_1= 0; field_2 = 0; 

                                                } 

                                } 

                }               

                publicvoid method2(int value) { 

                                synchronized(lock_2) { 

                                                synchronized(lock_1) { 

                                                                field_1= 0; field_2 = 0; 

                                                } 

                                } 

                } 

 

public class DeadLockTest {

    LinkedList list= new LinkedList(); 

     

    publicsynchronized void push(Object x) { 

       System.out.println("push");

       synchronized (list) { 

           list.addLast(x); 

            notify(); 

        } 

    } 

 

    publicsynchronized Object pop() throws Exception {   

       synchronized (list) { 

            if(list.size() <= 0) { 

               wait(); 

            } 

            returnlist.removeLast(); 

        } 

    } 

   

    public staticvoid main(String[] args) throws InterruptedException {

        finalDeadLockTest deadLockTest = new DeadLockTest();

        Threadthread = new Thread(new Runnable() {

            publicvoid run() {

                try{

                    deadLockTest.pop();

                }catch (Exception e) {

                   // TODO Auto-generated catch block

                   e.printStackTrace();

                }

            }

        });

       thread.start();

        Thread.sleep(2000);

        ThreadthreadPush = new Thread(new Runnable() {

            publicvoid run() {

                try{

                   deadLockTest.push("push thread");

                }catch (Exception e) {

                   // TODO Auto-generated catch block

                   e.printStackTrace();

                }

            }

        });

       threadPush.start();       

    }

}

 

当调用Thread.sleep()方法是 不会 释放对象锁, Object.wait 释放锁  wait(long timeout)方法,这个方法会让一个线程阻塞指定的timeout时间

Object 的协调支持方法: 只有在同步方法、同步语句中才能调用Object的协调方法。在调用Object的协调方法时,相关联的对象必须已经被加锁。

 

其实,在Java中任何一个对象均有一个锁,而syhchronized同步块在调用时会检测该对象的锁定情况,主过程和子过程两个线程竞争同一对象时,在主过程中调用wait()方法,子过程即可进入synchronized同步块。而当子过程调用notify方法时,主过程又重新获得了对象锁,继续执行下去。实现如下:

 

//主过程

public class MainProgross2 {

 private staticbyte[] sign = new byte[0];

 public static voidmain(String[] args) {

  SubProgrosssubProgross = new SubProgross();

  Thread subThread= new Thread(subProgross);

  long currentTime= System.currentTimeMillis();

 System.out.println("execute main progross!");

 subThread.start();

 synchronized(sign){

  System.out.println("main progross get lock!");

   try {

    sign.wait();

   } catch(InterruptedException e) {}

  }

 System.out.println("leave main progross!");

  long endTime =System.currentTimeMillis();

 System.out.println("total time: " + (endTime - currentTime));

 }

 

 //子过程

 static classSubProgross implements Runnable{

  public void run(){

  System.out.println("execute sub progross!");

   try {

   Thread.sleep(3000);

   } catch(InterruptedException e) { }

  synchronized(sign){

   System.out.println("sub progross get lock!");

    sign.notify();

   }

  System.out.println("leave sub progross!");

  }

 }

}

 

其实在Java的Object类中notifyAll、notify、wait方法均为native方法,在windows中是通过外部的dll来实现其内部,可以说Object的notify及wait等操作是JVM级的,而第一种方法是程序级的,理论上来说,后一种要更高效。

 

 

避免Java线程死锁

让所有的线程按照同样的顺序获得一组锁。这种方法消除了X 和Y 的拥有者分别等待对方的资源的问题。

将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。

将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。

 

1、在程序中尽量使用开放调用。依赖于开放调用的程序,相比于那些在持有锁的时候还调用外部方法的程序,更容易进行死锁自由度的分析。重新构建synchronized使开放调用更加安全。

所谓开放调用是指调用的方法本身没有加锁,但是要以对方法操作的内容进行加锁。

 

2、如果你必须获得多个锁,那么锁的顺序必须是你设计工作的一部分:尽量减少潜在锁之间的交互数量,遵守并文档化该锁顺序协议。监测代码中死锁自由度的策略有:

1)识别什么地方会获取多个锁,并使锁数量尽可能少,保证它们的顺序在程序中一致。

2)在没有非开放调用的程序中,发现那些获得多重锁的实例是非常简单的。

3、尝试定时的锁,使用每个显式Lock类中定时tryLock特性,来替代使用内部锁机制。

 

Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。不能保证多线程的执行有序性.可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。而最彻底的同步要保证有序性和可见性.

java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

Volatile变量修饰符如果使用恰当的话,它比synchronized的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。

 

处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值