美文网首页
java中关于线程的一些API

java中关于线程的一些API

作者: AlexYan_0360 | 来源:发表于2017-12-04 23:14 被阅读0次

    java中关于线程的一些API:

    * 线程的启动

        线程的启动有两种方式:继承Thread类,复写run方法;实现Runnable接口,实现run方法。

        两种方式都可以,但切记继承Thread类时如果要启动线程需要调用的是start方法而不是run方法,如果调用的是run方法的话不会开启新线程,只会在当前线程中执行run方法中的代码,如下面的代码

    public class ThreadStart {

    public static void main(String[] args) {

    System.out.println(Thread.currentThread().getName());

    ThreadRun t = new ThreadRun();

    t.run();

    //t.start();

    }

    }

    class ThreadRun extends Thread{

    @Override

    public void run(){

    System.out.println(Thread.currentThread().getName());

    }

    }

    上面的代码,如果调用的是run方法,得到的线程名字都是main线程,如果调用的是start方法得到的就是两个线程名字

    * 线程中断

        关于线程中断有三种方式:

        public void interrupt//中断

        public static boolean interrupted()//判断是否中断并清除中断标志位

        public boolean isInterrupted//判断是否中断

    关于中断和Thread中的stop方法是有区别的,调用stop方法是立刻停止线程,这样可能会造成数据不一致的现象,而调用interrupt方法是给当前线程添加中断标志,但不一定会立即中断

    第一个是实例方法,它通知目标线程中断,也就是设置中断标志位。中断标志位表示当前线程已经被中断了;第三个也是实例方法,它判断目标线程是否有被中断;第二个是静态方法,也是用来判断是否有被中断,但它会清除目标线程的中断标志位。

    public class ThreadInterruptDemo1 {

    public static void main(String[] args)throws InterruptedException {

    Thread t = new Thread(){

    @Override

    public void run(){

    while (true){

    Thread.yield();

    }

    }

    };

    t.start();

    Thread.sleep(2000);

    t.interrupt();

    }

    }

    在上述代码中,虽然对t进行了中断,但是线程并没有进行中断,如果想要中断需要进一步处理代码:

    public class ThreadInterruptDemo1 {

    public static void main(String[] args)throws InterruptedException {

    Thread t = new Thread(){

    @Override

    public void run(){

    while (true){

    if(Thread.currentThread().isInterrupted()){

    System.out.println("thread is interrupted");

    break;

    }

    Thread.yield();

    }

    }

    };

    t.start();

    Thread.sleep(2000);

    t.interrupt();

    }

    }

    这样就会中断退出。如果在循环体中出现类似wait或者sleep等操作时,只能通过中断来识别

    public class ThreadInterruptDemo1 {

    public static void main(String[] args)throws InterruptedException {

    Thread t = new Thread(){

    @Override

    public void run(){

    while (true){

    if(Thread.currentThread().isInterrupted()){

    System.out.println("thread is interrupted");

    break;

    }

    try {

    Thread.sleep(2000);

    }catch (InterruptedException e){

    System.out.println("Interrupt when sleep");

    //设置中断标志

    Thread.currentThread().interrupt();

    }

    Thread.yield();

    }

    }

    };

    t.start();

    Thread.sleep(2000);

    t.interrupt();

    }

    }

    Thread.sleep()方法由于中断而抛出的异常,它会清除中断标志,如果不加处理,那么在下一次循环中就无法捕获到这个中断标志位。

    * 等待(wait)和通知(notify)

        wait和notify必须使用在synchronized代码块中,当一个线程调用了wait方法时,这个线程就会进入等待状态并且会释放锁,当另外一个线程调用notify时,这个线程才会被唤醒(假设此处只有两个线程,并且等待队列中只有这一个线程)。

    注意事项:wait和sleep方法都是让线程进行等待,最重要的区别是wait方法会释放当前锁,而sleep不会;线程调用notify方法之后并不会立刻释放锁,只有当notify之后的代码执行完毕之后才会释放锁。

    public class ThreadWaitAndNotify {

    private static Object object = new Object();

    public static class T1 extends Thread{

    @Override

    public void run(){

    synchronized (object){

    System.out.println(System.currentTimeMillis()+" :T1 start!");

    try {

    System.out.println(System.currentTimeMillis()+" :T1 wait for object");

    object.wait();

    }catch (InterruptedException e){

    e.printStackTrace();

    }

    System.out.println(System.currentTimeMillis()+" :T1 end!");

    }

    }

    }

    public static class T2 extends Thread{

    @Override

    public void run(){

    synchronized (object){

    System.out.println(System.currentTimeMillis()+" :T2 start!");

    object.notify();

    System.out.println(System.currentTimeMillis()+" :T2 end!");

    try {

    Thread.sleep(2000);

    }catch (InterruptedException e){

    }

    }

    }

    }

    public static void main(String[] args) {

    Thread t1 = new T1();

    Thread t2 = new T2();

    t1.start();

    t2.start();

    }

    }

    运行结果如下:

    1512288311968 :T1 start!

    1512288311968 :T1 wait for object

    1512288311969 :T2 start!

    1512288311969 :T2 end!

    1512288313970 :T1 end!

    * 等待线程结束(join)和谦让(yield)

    很多时候,一个的输入可能非常依赖另外一个或多个线程的输出,此时,这个线程就需要等待依赖线程执行完毕,才能继续执行。JDK提供了两个方法:

    public final synchronized void join(long millis)//最大等待时间,当超过这个时间主线程就会继续执行

    public final void join()//无限期等待,直到目标线程执行完毕

    yield:

    public static native void yield()

    这是一个静态方法,一旦执行,它会使当前线程让出CPU,但要注意,让出CPU并不表示当前线程不执行了。当前线程在让出CPU后,还会进行CPU资源的争夺。

    * 守护线程(daemon)

        当一个应用中只有守护线程时,表示这个java虚拟机就会自然退出

    注意事项:设置守护线程时一定要设置在start方法之前,否则会报IllegalThreadStatException异常,报出这个异常并不会使线程停止,该线程还会继续执行。

    * 线程优先级

        在Thread类中有表示线程优先级的字段

        public final static int MIN_PRIORITY = 1;

        public final static int NORM_PRIORITY = 5;

        public final static int MAX_PRIORITY = 10;

        数值越大,优先级越高。

    * 线程安全(synchronized)

        并行程序的开发是以线执行结果的争取性为保证的,所以线程安全是并行程序开发额根本和根基。以下这个程序就是典型的线程不安全

    public class ThreadSynchronizeDemo1 implements Runnable {

    static ThreadSynchronizeDemo1 instance = new ThreadSynchronizeDemo1();

    static volatile int i = 0;

    public void increase(){

    i++;

    }

    @Override

    public void run(){

    for(int j = 0; j < 1000000; j++){

    increase();

    }

    }

    public static void main(String[] args)throws InterruptedException {

    Thread t1 = new Thread(instance);

    Thread t2 = new Thread(instance);

    t1.start();

    t2.start();

    t1.join();

    t2.join();

    System.out.println(i);

    }

    }

    上述的结果会小于2000000,线程t1和t2同时读取i为0,并各自计算得到i=1,并先后写入这个结果,因此,虽然i++被执行了2次,但是实际i的值只增加了1,想解决这个问题synchronized关键字是一个解决方案。

    synchronized可以有多种用法:

        [x] 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁

        [x] 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁

        [x] 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁

    这时对上述代码进行改造:

    public synchronized void increase(){

    i++;

    }

    在这个实例方法上加synchronized关键字进行同步

    在上述代码尤其要特别注意:在创建线程时使用的是同一个对象实例,如果此时使用的两个不同的对象实例,在increase()方法上加synchronized关键字是不起作用的,如果想得到正确的结果而又是不同的对象实例,可以在increase()方法之前加上static关键字,让这个方法变为静态方法,这个synchronized关键字就会作用于这个类上。

    * 并发下的list

        JDK中的util包中的list是线程不安全的类,如下面的代码:

    public class ThreadList {

    static List<Integer> list = new ArrayList<>();

    static class AddThread implements Runnable{

    @Override

    public void run(){

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

    list.add(i);

    }

    }

    }

    public static void main(String[] args) throws InterruptedException {

    Thread t1 = new Thread(new AddThread());

    Thread t2 = new Thread(new AddThread());

    t1.start();

    t2.start();

    t1.join();

    t2.join();

    System.out.println(list.size());

    }

    }

    上面的代码可能会出现三种情况:

    第一种:程序正常结束

    第二种:抛出异常:Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 15  这是因为ArrayList在扩容过程中,内部一致性被破坏,但由于没有锁的保护,另外一个线程访问到了不一致的内部状态,导致出现越界问题。

    第三种:程序正常结束,但是结果不正确。

    相关文章

      网友评论

          本文标题:java中关于线程的一些API

          本文链接:https://www.haomeiwen.com/subject/iynfixtx.html