Java多线程

来源:互联网 发布:女生项链品牌 知乎 编辑:程序博客网 时间:2024/05/16 04:38

一、线程总是属于某个进程,进程中的多个线程共享进程的内存。“同时”执行是人的感觉,在线程之间实际上轮换执行。线程总体分两类:用户线程和守候线程。当所有用户线程执行完毕的时候,JVM自动关闭。但是守候线程却不独立于JVM,守候线程一般是由操作系统或者用户自己创建的。

二、在调用start()方法之后:发生了一系列复杂的事情:在调用start()方法之后:发生了一系列复杂的事情:启动新的执行线程(具有新的调用栈);该线程从新状态转移到可运行状态;当该线程获得机会执行时,其目标run()方法将运行。

这里写图片描述

1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。
5、死亡态:当线程的run()方法完成时就认为它死去。

这里写图片描述

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

1、睡眠

静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。睡眠的实现:调用静态方法
sleep()是静态方法,只能控制当前正在运行的线程。

2、线程的优先级和线程让步yield()

线程的让步是通过Thread.yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。
使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。

3、join()方法

Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。
可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。

4、线程同步synchronized

如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步
线程睡眠时,它所持的任何锁都不会释放。
同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。

public int fix(int y) {             synchronized (this) {                     x = x - y;             }                  return x;     } 

这里写图片描述

6、线程的同步与锁,线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

7、要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。

这里写图片描述

8、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。

9、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。

10、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。

11、何时需要同步,在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。

三、线程安全类
当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。即使是线程安全类,也应该特别小心,因为操作的线程之间仍然不一定安全。举个形象的例子,比如一个集合是线程安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作,也许在第一个线程查询后,第二个线程也查询出集合非空,但是当第一个执行清除后,第二个再执行删除显然是不对的,因为此时集合已经为空了。

这里写图片描述
这里写图片描述
解决上面问题的办法是,在操作集合对象的NameList上面做一个同步。改写后的代码如下:
这里写图片描述

1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。

四、线程的交互

1、voidnotify()
唤醒在此对象监视器上等待的单个线程。
2、voidnotifyAll()
唤醒在此对象监视器上等待的所有线程。
3、voidwait()
导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法
4、当在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时,并不意味着这时线程会放弃其锁。如果线程荣然在完成同步代码,则线程在移出之前不会放弃锁。因此,只要调用notify()并不意味着这时该锁变得可用。

线程的调度-休眠
线程休眠的目的是使线程让出CPU的最简单的做法之一,线程休眠时候,会将CPU资源交给其他线程,以便能轮换执行,当休眠一定时间后,线程会苏醒,进入准备状态等待执行。

线程的调度-优先级
与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。
线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5。

五、Java 安全模型介绍

1、在Java中将执行程序分成本地和远程两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱(Sandbox)机制。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统的资源访问,通过这样的措施来保证对远程代码的有效隔离,防止对本地系统造成破坏。

2、在Java1.2版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如图3所示

这里写图片描述

3、当前最新的安全机制实现,则引入了域(Domain)的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(ProtectedDomain),对应不一样的权限(Permission)。

这里写图片描述

 package learn.java.security;  import java.io.File;  import java.io.IOException;  import java.security.AccessControlException;  import java.security.AccessController;  import java.security.PrivilegedAction;  public class FileUtil {     // 工程 A 执行文件的路径     private final static String FOLDER_PATH = "D:\\workspace\\projectX\\bin";     public static void makeFile(String fileName) {         try {             // 尝试在工程 A 执行文件的路径中创建一个新文件            File fs = new File(FOLDER_PATH + "\\" + fileName);             fs.createNewFile();         } catch (AccessControlException e) {             e.printStackTrace();         } catch (IOException e) {             e.printStackTrace();         }     public static void doPrivilegedAction(final String fileName) {         // 用特权访问方式创建文件        AccessController.doPrivileged(new PrivilegedAction<String>() {             @Override             public String run() {                 makeFile(fileName);                 return null;             }         });     }  }

2、Void wait(long timeout,int nanos)

导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

package learn.java.security;/** * Java线程:线程的同步 */public class Test{    public static void main( String[] args )    {        User u = new User( "张三" , 100 );        MyThread t1 = new MyThread( "线程A" , u , 20 );        MyThread t2 = new MyThread( "线程B" , u , -60 );        MyThread t3 = new MyThread( "线程C" , u , -80 );        MyThread t4 = new MyThread( "线程D" , u , -30 );        MyThread t5 = new MyThread( "线程E" , u , 32 );        MyThread t6 = new MyThread( "线程F" , u , 21 );        t1.start();        t2.start();        t3.start();        t4.start();        t5.start();        t6.start();    }}class MyThread extends Thread{    private User u;    private int y = 0;    MyThread( String name , User u , int y )    {        super( name );        this.u = u;        this.y = y;    }    public void run()    {        u.oper( y );    }}class User{    private String code;    private int cash;    User( String code , int cash )    {        this.code = code;        this.cash = cash;    }    public String getCode()    {        return code;    }    public void setCode( String code )    {        this.code = code;    }    /** * 业务方法 * @param x 添加x万元 */    public synchronized void oper( int x )    {        try        {            Thread.sleep( 10L );            this.cash += x;            System.out.println(                    Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash );            Thread.sleep( 10L );        }        catch ( InterruptedException e )        {            e.printStackTrace();        }    }    public String toString()    {        return "User{" + "code='" + code + '\'' + ", cash=" + cash + '}';    }}/** * 业务方法 * * @param x 添加x万元 */    public void oper( int x )    {        try        {            Thread.sleep( 10L );            synchronized ( this )            {                this.cash += x;                System.out.println(                        Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash );            }            Thread.sleep( 10L );        }        catch ( InterruptedException e )        {            e.printStackTrace();        }    }    public String toString()    {        return "User{" + "code='" + code + '\'' + ", cash=" + cash + '}';    }}

六、新特征-有返回值的线程

在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。现在Java终于有可返回值的任务(也可以叫做线程)了。可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。

package learn.java.security;import java.util.concurrent.*;/** * Java线程:有返回值的线程 * * @author Administrator 2009-11-5 0:41:50 */public class Test{    public static void main( String[] args ) throws ExecutionException , InterruptedException    {        // 创建一个线程池        ExecutorService pool = Executors.newFixedThreadPool( 2 );        // 创建两个有返回值的任务        Callable<String> c1 = new MyCallable( "A" );        Callable<String> c2 = new MyCallable( "B" );        // 执行任务并获取Future对象        Future<String> f1 = pool.submit( c1 );        Future<String> f2 = pool.submit( c2 );        // 从Future对象上获取任务的返回值,并输出到控制台        System.out.println( ">>>" + f1.get().toString() );        System.out.println( ">>>" + f2.get().toString() );        // 关闭线程池        pool.shutdown();    }}class MyCallable implements Callable<String>{    private String oid;    MyCallable( String oid )    {        this.oid = oid;    }    public String call() throws Exception    {        return oid + "任务返回的内容";    }}
原创粉丝点击