Java高并发之线程基本操作

结合上一篇同步异步,这篇理解线程操作。

1、新建线程。不止thread和runnable,Callable和Future了解一下

package com.thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * 线程启动
 * @author Administrator
 *
 */
public class StartThreadTest extends Thread{

    @Override
    public void run(){
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"i:"+i);
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        //继承 Thread 类的线程启动方式
        StartThreadTest test = new StartThreadTest();
        StartThreadTest test2 = new StartThreadTest();
        System.out.println("开始执行线程1......");
        test.start();test2.start();
        test.join();test2.join();  //join方法会让main线程等待test、test2线程执行完,如果对执行结果不感冒,则不用join
        System.out.println("结束执行线程1......");
    }
    //******************************************************************************************************
    static class startThread2 implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("i:"+i);
            }
        }
        
        public static void main(String[] args) {
            //实现 Runnable 接口的线程启动方式
            startThread2 thread2 = new startThread2();
            Thread t = new Thread(thread2);
            t.start();
        }
    }
    //******************************************************************************************************
    /**
     * Callable 用于获取线程执行结果
     *     多个Callable耗时是每个线程的总耗时
     * @author Administrator
     *
     */
    static class startThread3 implements Callable<String>{

        @Override
        public String call() throws Exception {
            int result = 0;
            for (int i = 0; i < 3; i++) {
                Thread.sleep(1000);
                result += i;
            }
            return String.valueOf(result);
        }
        
        public static void main(String[] args) throws Exception {
            SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
            System.out.println("开始时间:"+format.format(new Date()));
            
            Callable<String> call = new Callable<String>() {
                
                @Override
                public String call() throws Exception {
                    int result = 0;
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(1000);
                        result += i;
                    }
                    return String.valueOf(result);
                }
            };
            ExecutorService service = Executors.newSingleThreadExecutor();
            Future<String> f = service.submit(call);
            System.out.println(f.get());
            service.shutdown();
            System.out.println("时间1:"+format.format(new Date()));
            //实现 Callable 接口的线程启动方式
            startThread3 thread3 = new startThread3();
            System.out.println("开始执行线程......");
            String result = thread3.call();
            System.out.println("结束执行线程。result:"+result);  //result:3 【线程执行完获取结果,会阻塞当前线程】
            System.out.println("时间2:"+format.format(new Date()));
            
            //开始时间:05:04:21
            //10
            //时间1:05:04:26
            //开始执行线程......
            //结束执行线程。result:3
            //时间2:05:04:29

        }
    }
    //******************************************************************************************************
    /**
     * Future 用于获取异步线程的结果,总耗时是最长耗时的线程
     * @author Administrator
     *
     */
    static class startThread4 {
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
            System.out.println("开始时间:"+format.format(new Date()));
            
            //操作1:耗时3秒
            FutureTask<String> futureTask = new FutureTask<>(new startThread3());
            new Thread(futureTask).start();
            
            //操作2:耗时5秒
            Callable<String> call = new Callable<String>() {
                
                @Override
                public String call() throws Exception {
                    int result = 0;
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(1000);
                        result += i;
                    }
                    return String.valueOf(result);
                }
            };
            FutureTask<String> futureTask2 = new FutureTask<>(call);
            new Thread(futureTask2).start();
            
            System.out.println("操作1返回:"+futureTask.get());
            System.out.println("操作2返回:"+futureTask2.get());
            
            System.out.println("结束时间:"+format.format(new Date()));
            
            //开始时间:04:50:53
            //操作1返回:3
            //操作2返回:10
            //结束时间:04:50:58
        }
    }
}
View Code

2、停止线程。volatile和interrupt各显神通

package com.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 关闭线程
 * @author Administrator
 *
 */
public class StopThreadTest extends Thread {

    static int i = 0;
    static int j = 0;

    @Override
    public void run() {
        //同步操作确保i,j都自增1
        synchronized (this) {
            i++;
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        StopThreadTest test = new StopThreadTest();
        test.start();
        Thread.sleep(1000);
        //Thread.stop():强制关闭对象获取的锁,破坏线程数据安全
        test.stop();
        test.join();
        System.out.println("i:"+i+";j:"+j);  //i:1;j:0
        
    }
    
    static class volatileStopThreadTest extends Thread{
        
        volatile boolean exitFlag = false;  //volatile确保可见性
        static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
        
        @Override
        public void run() {
            System.out.println("run线程开始时间:"+format.format(new Date()));
            while(!exitFlag){
                System.out.println("run方法进入时间:"+format.format(new Date()));
                //同步操作确保i,j都自增1
                synchronized (this) {
                    i++;
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    j++;
                }
            }
            System.out.println("run线程结束时间:"+format.format(new Date()));
        }
        
        public static void main(String[] args) throws InterruptedException {
            System.out.println("主线程开始时间:"+format.format(new Date()));
            volatileStopThreadTest test = new volatileStopThreadTest();
            test.start();
            Thread.sleep(1000);
            System.out.println("主线程休眠1秒:"+format.format(new Date()));
            test.exitFlag = true;
            test.join();
            System.out.println("i:"+i+";j:"+j);
            
            //主线程开始时间:05:17:03
            //run线程开始时间:05:17:03
            //run方法进入时间:05:17:03
            //主线程休眠1秒:05:17:04   【标志位exitFlag改了,run方法依然在执行,只是下次竞争到锁都不执行run】
            //run线程结束时间:05:17:13
            //i:1;j:1  【确保了线程数据安全】

        }
    }
    
    static class interruptStopThreadTest extends Thread{
        
        static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
        
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"-线程进入run开始时间:"+format.format(new Date()));
            while(true){
                //判断线程是否已经被中断,如果被中断,则下次竞争时直接退出
                if(Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"-线程已被中断......,中断时间:"+format.format(new Date()));
                    //退出while循环
                    break;
                }
                //同步操作确保i,j都自增1
                synchronized (this) {
                    i++;
                    try {
                        Thread.sleep(10000);
                        System.out.println(Thread.currentThread().getName()+"-线程休眠10秒......");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.println("中断标志:"+Thread.currentThread().isInterrupted());
                        //阻塞过程中被中断,test.interrupt()方法设置的中断标识会立刻变成false,需要重新设置中断标识
                        Thread.currentThread().interrupt();
                        System.out.println(Thread.currentThread().getName()+"-线程被中断了!");
                    }
                    j++;
                }
                //打印:
                //Thread-0-线程进入run开始时间:10:01:34
                //java.lang.InterruptedException: sleep interrupted
                //    at java.lang.Thread.sleep(Native Method)
                //    at com.thread.StopThreadTest$interruptStopThreadTest.run(StopThreadTest.java:103)
                //中断标志:false
                //Thread-0-线程被中断了!
                //Thread-0-线程已被中断......,中断时间:10:01:36
                //i:1;j:1  【线程数据安全】

//                synchronized (this) {
//                    i++;
//                    try {
//                        for (int z = 0; z < 10; z++) {
//                            if(z==9){
//                                int a = 1/0;
//                                System.out.println("模拟异常");
//                            }
//                        }
//                        Thread.sleep(5000); //异常后的语句不会执行
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                        //线程执行过程中抛出了异常,通过设置中断标识,控制下次竞争
//                        Thread.currentThread().interrupt();
//                        System.out.println(Thread.currentThread().getName()+"-线程被中断了!");
//                    }
//                    j++;
//                }
                //打印:
                //Thread-0-线程进入run开始时间:10:08:43
                //Thread-0-线程被中断了!
                //java.lang.ArithmeticException: / by zeroThread-0-线程已被中断......,中断时间:10:08:43

                //    at com.thread.StopThreadTest$interruptStopThreadTest.run(StopThreadTest.java:126)
                //i:1;j:1  【线程数据安全】

            }
        }
        
        public static void main(String[] args) throws InterruptedException {
            interruptStopThreadTest test = new interruptStopThreadTest();
            test.start();
            Thread.sleep(2000);
            test.interrupt();  //中断线程 【设置中断标志,通过isInterrupted()控制竞争】
            test.join();
            System.out.println("i:"+i+";j:"+j);
        }
    }
}
View Code

 3、线程挂起和继续执行。suspend和resume谨慎使用

package com.thread;

/**
 * 线程挂起与继续执行
 *     不推荐用:
 *         1、锁得不到释放,如果第二个线程先于第一个线程执行resume之前执行start,将造成锁永久等待
 *         2、锁住的共享资源如果暗含同步方法,将导致其他线程使用该同步方法等待
 * @author Administrator
 *
 */
public class SuspendANDresumeThreadTest extends Thread{

    volatile int i = 0;
    
    @Override
    public void run(){
        while(true){
            synchronized(this){
                i++;
                System.out.println(Thread.currentThread().getName()+"-i:"+i);
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        SuspendANDresumeThreadTest test = new SuspendANDresumeThreadTest();
        SuspendANDresumeThreadTest test2 = new SuspendANDresumeThreadTest();
        test.start();
        Thread.sleep(1);
        //test线程挂起后,占有的锁不会释放,导致test2线程一直处于阻塞态
        test.suspend();
//        test2.start();
//        test.join();
        //另外:查看println源码得知该方法是同步方法,在test占有锁的同时,导致println()也被占有,从而main线程无法执行下面的打印语句
        System.out.println("该句无法打印!");
        
    }
}
View Code

4、等待线程结束和谦让。join和yield

package com.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * join:当前线程等待执行join的线程执行完
 * yield:执行yield的线程让出占用的锁
 * @author Administrator
 *
 */
public class JoinANDyieldThreadTest extends Thread{

    static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
    
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"-i:"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        JoinANDyieldThreadTest test = new JoinANDyieldThreadTest();
        JoinANDyieldThreadTest test2 = new JoinANDyieldThreadTest();
        
//        test.start();
//        Thread.sleep(3000);
//        test.yield();
//        test2.start();
        
        //Thread-0-i:0
        //Thread-0-i:1
        //Thread-0-i:2  
        //Thread-0-i:3  【执行test.yield()后,test依然抢到了锁】
        //Thread-1-i:0
        //Thread-1-i:1
        //Thread-0-i:4
        
        test.setPriority(MIN_PRIORITY);  //低优先级
        test2.setPriority(MAX_PRIORITY);  //高优先级
        System.out.println("开始时间:"+format.format(new Date()));
        test.start();
        Thread.sleep(3000);
        test.yield();
        test2.start();
        test.join();
        test2.join();  //确保下面的打印在test和test2执行完后执行
        System.out.println("结束时间:"+format.format(new Date()));
        //Thread-0-i:0
        //Thread-0-i:1
        //Thread-0-i:2
        //Thread-0-i:3  【test设置低优先级依然抢到了锁】
        //Thread-1-i:0
        //Thread-0-i:4
        //Thread-1-i:1
        //Thread-0-i:5
        
    }
}
View Code

5、线程等待和唤醒。wait和notify

package com.thread;

/**
 * wait 和 notify 设计生产者与消费者模式
 *     锁的选用:线程共享的变量
 *     wait 和 notify时机的运用,确保先生产后消费
 * @author Administrator
 *
 */
public class WaitANDnotifyThreadTest {
    private static Integer count = 0; // 数据仓库计数
    private final static Integer FULL = 5; // 数据仓库最大存储量
    private static String lock = "lock"; // 锁标识
    // Object obj = new Object();  //锁标识

    public static void main(String[] args) {
        new Thread(new Producer(), "product1").start();
        new Thread(new Consumer(), "consumer1").start();
        new Thread(new Producer(), "product2").start();
        new Thread(new Consumer(), "consumer2").start();
    }

    // 生产者
    static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (lock) {
                    while (count == FULL) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName() + "-produce:: " + count);
                    // 唤醒lock锁上的所有线程
                    lock.notifyAll();
                }
            }
        }
    }

    // 消费者
    static class Consumer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (lock) {
                    // 如果首次消费者竞争得到锁,进入后等待
                    while (count == 0) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "-consume:: " + count);
                    lock.notifyAll();
                }
            }
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/x-jingxin/p/10319775.html