初识多线程(java编程思想)

道理以后会懂,先写写法吧。

任务

线程的创建

创建的基本用法

//创建线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        });//新建线程
        //lambda
        Thread t2 = new Thread(()->System.out.println("hello world!"));
        //t1.run(); 此方法不会创建新线程 在主线程执行
        t1.start();//此方法创建新线程,在新线程中进行
        t2.start();

不能直接调用t1.run()方法,必须采用start()才能创建

接口或继承

class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println( Thread.currentThread().getName()+ " " + i);
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        super.run();
        System.out.println( Thread.currentThread().getName()+ " " + i);
    }
}

线程阻塞

线程运行到该语句时会停止执行一段时间 Thread.sleep(1000);阻塞一秒,或者 TimeUnit.MILLISECONDS.sleep(1000);

线程让步

yield();表示该线程已经执行了一些操作,可以转到其他线程了

Executor

管理Thread对象,简化并发编程,先看简单用法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//Executor 执行器 简化了并发编程

public class LiftOff implements Runnable{
    protected int downCount = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    public LiftOff(){}
    public LiftOff(int count){
        downCount = count;
    }
    public String status(){
        return "#" + id + "(" +(downCount>0?downCount:"LiftOff!") + ")";
    }

    @Override
    public void run() {
        while(downCount-->0){
            System.out.println(status());
            try{
                //Thread.sleep(1000);//线程休眠  阻塞 单位毫秒 1000毫秒 及一秒
                //现在的写法
                TimeUnit.MILLISECONDS.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        //Thread.yield();//可以换其他线程   让步,但是结果并不确定,不能依赖
    }

    public static void main(String...args){
        LiftOff l= new LiftOff();
        //l.run();
//        for(int i = 0;i<5;i++)
//            协作
//            new Thread(new LiftOff()).start();
        System.out.println("-------");
        //以上代码可以执行,以下是对Executor 执行器的简单介绍
        ExecutorService exec =
        		//下面是几种线程池的简单介绍
                //事先建好需要的线程   速度快
                Executors.newFixedThreadPool(5);//事先创建5个线程准备
                //执行过程中创建需要的线程
                //Executors.newCachedThreadPool();
                //相当于个数为1的FixedThreadPool 需要排队
                //Executors.newSingleThreadExecutor();
        for(int i =0;i<5;i++)
            exec.execute(new LiftOff());//提交任务
        exec.shutdown();//之后不可以提交新的任务 程序在执行完所有的任务后退出
    }
}

Executor的异常处理

try-catch块不能捕获到异常,如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//利用ExecutorService捕获异常
//try-catch捕获不到
public class ExceptionThread implements Runnable{
    @Override
    public void run() {
        throw new RuntimeException();
    }
    public static void main(String...args){
        ExecutorService ece=null;
        //ok
        try{
            ExceptionThread t = new ExceptionThread();
            t.run();
        }catch (Exception e){
            System.out.println("catch");
        }
        
        //wrong
        try{
            ece = Executors.newCachedThreadPool();
            ece.execute(new ExceptionThread());
        }catch (Exception e){
            System.out.println("catch");
        }finally {
            if(ece!=null)
                ece.shutdown();
        }
    }
}

Exception in thread “pool-1-thread-1” java.lang.RuntimeException
at com.concurrency.ExceptionThread2.run(CaptureUncaughtException.java:28)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:835)

要对Executor进行异常处理,采用Thread.UncaughtExceptionHandler接口

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
//Executor处理异常的方式
public class CaptureUncaughtException {
    public static void main(String...args){
        //两种处理方式
        //线程工厂
        ExecutorService ec = Executors.newCachedThreadPool(new HandlerThreadFactory());
        ec.execute(new ExceptionThread2());
        ec.shutdown();
        //静态方法,为线程创建异常的默认处理方式
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        ExecutorService ecc = Executors.newCachedThreadPool();
        ecc.execute(new ExceptionThread2());
        ecc.shutdown();
    }
}
class ExceptionThread2 implements Runnable{
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run by "+t);
        //getUncaughtExceptionHandler()得到处理异常的类
        System.out.println("eh = "+t.getUncaughtExceptionHandler());
        throw new RuntimeException();//抛出异常
    }
}

//异常处理
//继承接口,重写处理方法
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("catch " + e);
    }
}
//线程工厂,为每一个线程创建异常处理器
//setUncaughtExceptionHandler(new MyUncaughtExceptionHandler())
class HandlerThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        System.out.println(this + " creating new Thread");
        Thread t = new Thread(r);
        System.out.println("created "+t);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh = "+t.getUncaughtExceptionHandler());
        return t;
    }
}

任务中的返回值

必须使用Executor.submit()调用,使用call()而不是run(); callable接口,具有类型参数的泛型 泛型为call的返回值类型 submit的返回值类型为Future< type > type为call()的返回值类型
import java.util.ArrayList;
import java.util.concurrent.*;

public class CallableDemo {
    public static void main(String...args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        ArrayList<Future<String>> results =
                new ArrayList<>();
        for (int i = 0; i < 10; i++)
            //submit产生future对象,用返回值类型进行参数化
            results.add(exec.submit(new TaskWithResult(i)));
        for (Future<String> fs : results) {
            try {
                //get() blocks until completion:
                //可以调用isDone判断future结果是否准备就绪
                //准备好之前调用get()将阻塞
                System.out.println(fs.get());
            } catch (InterruptedException e) {
                System.out.println(e);
                return;//???
            } catch (ExecutionException e) {
                System.out.println(e);
            } finally {
                exec.shutdown();
            }
        }
    }
}
//具有类型参数的泛型 泛型为call的返回值类型
class TaskWithResult implements Callable<String>{
    private int id;
    public TaskWithResult(int id){
        this.id = id;
    }
    public String call(){
        return "result of TaskWithResult " + id;
    }
}

优先级

这里运行的结果和预想的不同,理解代码,以后再看
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimplePriorities implements Runnable {
    private int countDown = 5;
    private volatile double d;
    private int priority;

    public SimplePriorities(int priority) {
        this.priority = priority;
    }

    @Override
    public String toString() {
        return Thread.currentThread() + ": "
                + countDown;
    }
    @Override
    public void run() {
        //优先级在run()方法里面声明,在构造函数中声明没有用
        Thread.currentThread().setPriority(priority);
        while (true) {
            for (int i = 0; i < 100000; i++) {
                d += (Math.PI + Math.E) / (double) i;
                if (i % 1000 == 0)
                    Thread.yield();//让步 只是一个暗示
            }
            System.out.println(this);
            if (--countDown == 0) return;
        }
    }

    public static void main(String... args) {
        //没发现区别。。。
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
        for (int i = 0; i < 5; i++)
            exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));

        exec.shutdown();
    }
}

后台线程

先熟悉写法,意义以后理解,概念看书

非后台线程结束后会杀死后台线程,所以后台线程run()方法的finally可能会得不到执行,比如main()方法就是非后台线程,改变main()中的sleep() 阻塞时间 可以看到后台线程的启动过程

在线程start方法前调用setDaemon(true)来设置为后台线程

一旦被声明为后台线程,它创建的子线程也是后台线程,不再需要设置

下面代码还用到了线程工厂来创建后台线程,注释掉的部分为不用工厂的创建过程

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//后台线程
public class SimpleDaemons implements Runnable{
    @Override
    public void run() {
        try {
            while(true){
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread()+" "+this);

                //后台线程创建的线程还是后台线程,不再需要手动设置
                Thread td = new Thread(new Daemon());
                td.start();
                System.out.println("td.isDaemon():"+td.isDaemon()+" Daemon:"+td);
            }
        }catch(InterruptedException e){
            System.out.println("sleep() interrupted");
        }finally{
            //没有执行!!!
            //主线程结束后直接粗暴的终止所有后台线程
            System.out.println("this is finally");
        }
    }
    public static void main(String...args) throws Exception{
        //利用线程工厂
        //接受的对象工厂用来创建新的线程
        ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());
        for(int i = 0;i<10;i++){

            exec.execute(new SimpleDaemons());//线程工厂创建线程;

            //Thread daemon = new Thread(new SimpleDaemons());
            //daemon.setDaemon(true);//将线程设置为后台线程 必须在启动之前,即调用start()方法前

            //daemon.start();
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(500);//main()线程先停一段时间,可以看到后台线程的启动过程
        //main()结束,杀掉所有后台线程
    }
}
//在后台线程中创建的子线程
class Daemon implements Runnable{
    @Override
    public void run() {
        while(true){}
    }

    @Override
    public String toString() {
        return "hello,"+Thread.currentThread().getName();
    }
}

join

在第一个线程上的第二个线程调用join(),则执行第二个,第一个线程被挂起,执行结束才返回开始执行第一个 有参数的join(1000); 1000ms之后不管第二个线程有没有结束都会返回,第一个线程开始,但此时的第二个线程仍然在执行至结束 try-catch会清除sleep被打断后的中断状态,所以返回false
//join() 在第一个线程上的第二个线程调用join,则执行第二个,执行结束才返回
class Sleeper extends Thread{
    private int duration;
    public Sleeper(String name,int sleeptime){
        super(name);
        duration = sleeptime;
        start();
    }
    public void run(){
        try{
            sleep(duration);
        }catch (InterruptedException ie){
            System.out.println(getName()+" was interrupted. "+
                    "isInterrupted():"+isInterrupted());
            return;
        }
        System.out.println(getName()+" has awakened");
    }
}

class Joiner extends Thread{
    private Sleeper sleeper;
    public Joiner(String name,Sleeper sleeper){
        super(name);
        this.sleeper = sleeper;
        start();
    }
    public void run(){
        try{
            //可以给join加一个超时参数使之可以返回
            //加上1000参数,先返回complemented,再返回awaked
            //1000ms之后,joiner开始执行,但是sleeper没有停止
            //不加参数,先执行完join 再执行complemented
            sleeper.join(1000);//直到sleeper()结束之后Joiner线程才会继续下去
        }catch(InterruptedException e){
            System.out.println("Interrupted!");
        }
        System.out.println(getName()+" join complemented");
    }
}

public class Joining{
    public static void main(String...args){
        Sleeper sleeper = new Sleeper("sleepy",1500);
        Sleeper sleeper1 = new Sleeper("Grumpy",1500);
        Joiner dopey = new Joiner("Dopey",sleeper);
        Joiner doc = new Joiner("Doc",sleeper1);
        sleeper1.interrupt();//打断join()方法

    }
}

有响应的“用户界面”

多线程的一个用法 在计算的同时可以监听控制台输入,不会阻塞
public class ResponiveUI extends Thread {
    private static volatile double d = 1;
    public ResponiveUI(){
        setDaemon(true);
        start();
    }
    public void run(){
        while(true){
            d+=(Math.E+Math.PI)/d;
            if(d>100000)
                break;
        }
    }
    public static void main(String[]args)throws Exception{
        new ResponiveUI();//后台任务,不影响后面的read();
        System.in.read();
        System.out.println(d);
    }
}

如果单线程执行,只能等到d>100000才能执行下面的输入,把它设为后台线程,就可以直接执行输入,不在需要等待,如果while中是个死循环,单线程永远到不了输入

内部类的运用

InnerThread1,InnerThread2运用继承
InnerRunnable1,InnerRunnable1运用runnable接口
ThreadMethod在方法里运用匿名内部类
import java.util.concurrent.TimeUnit;

public class ThreadVariations {
    public static void main(String[]args){

        new InnerThread1("InnerThread1");
        new InnerThread2("InnerThread2");
        new InnerRunnable1("InnerRunnable1");
        new InnerRunnable2("InnerRunnable2");
        new ThreadMethod("ThreadMethod").runTask();

    }
}
class InnerThread1{
    private int countDown = 5;
    private Inner inner;
    private class Inner extends Thread {
        Inner(String name) {
            super(name);
            start();
        }
        @Override
        public void run() {
            try{
                while(true){
                    System.out.println(this);
                    if(--countDown==0)
                        return;
                    sleep(10);
                }
            }catch(InterruptedException ie){
                System.out.println("interrupted!");
                ie.printStackTrace();
            }
        }
        @Override
        public String toString(){
            return getName() + ":" + countDown;
        }
    }
    public InnerThread1(String name){
        inner = new Inner(name);
    }
}

class InnerThread2 {
    private int countDown = 5;
    private Thread t;

    public InnerThread2(String name) {
        t = new Thread(name) {
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println(this);
                        if (--countDown == 0)
                            return;
                        sleep(10);
                    }
                } catch (InterruptedException ie) {
                    System.out.println("interrupted!");
                    ie.printStackTrace();
                }
            }
            @Override
            public String toString() {
                return getName() + ":" + countDown;
            }
        };
        t.start();
    }
}
class InnerRunnable1{
    private int countDown = 5;
    private Inner inner;
    private class Inner implements Runnable {
        Thread t;
        Inner(String name) {
            t=new Thread(this,name);
            t.start();
        }
        @Override
        public void run() {
            try{
                while(true){
                    System.out.println(this);
                    if(--countDown==0)
                        return;
                    TimeUnit.MILLISECONDS.sleep(10);
                }
            }catch(InterruptedException ie){
                System.out.println("interrupted!");
                ie.printStackTrace();
            }
        }
        @Override
        public String toString(){
            return t.getName() + ":" + countDown;
        }
    }
    public InnerRunnable1(String name) {
        inner = new Inner(name);
    }
}

class InnerRunnable2 {
    private int countDown = 5;
    private Thread t;
    public InnerRunnable2(String name) {
        t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println(this);
                        if (--countDown == 0)
                            return;
                        TimeUnit.MILLISECONDS.sleep(10);
                    }
                } catch (InterruptedException ie) {
                    System.out.println("interrupted!");
                    ie.printStackTrace();
                }
            }
            @Override
            public String toString() {
                return Thread.currentThread().getName() + ":" + countDown;
            }
        },name);
        t.start();
    }
}
class ThreadMethod{
    private int countDown = 5;
    private Thread t;
    private String name;
    public ThreadMethod(String name){
        this.name = name;
    }
    public void runTask(){
        if(t==null){
            t = new Thread(name){
                public void run(){
                    try{
                        while(true){
                            System.out.println(this);
                            if(--countDown==0)
                                return;
                            sleep(10);
                        }
                    }catch(InterruptedException ie){
                        System.out.println("interrupted!");
                        ie.printStackTrace();
                    }
                }
                public String toString(){
                    return getName() + ":" + countDown;
                }
            };
            t.start();
        }
    }
}

受限资源的共享

下面一个程序 EvenGenerator.next()方法把currentEvenValue加2,但输出的时候竟然输出了奇数。在for循环里面创建了多个线程,同时对currentEvenValue修改,可能该线程在进行奇偶判断时,其他线程正好执行完+1,就出现了奇数,共享的资源产生混乱

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EvenChecker implements Runnable {
    private final int id;
    private IntGenerator generator;
    public EvenChecker(IntGenerator g,int id){
        this.id = id;
        generator = g;
    }
    @Override
    public void run() {
        while(!generator.isCanceled()){
            int val = generator.next();
            if(val%2!=0){
                System.out.println(val + " not even!");
                generator.cancel();
            }
        }
    }
    public static void test(IntGenerator gp,int count) {
        System.out.println("Press Control-C to exit");
        ExecutorService ec = Executors.newCachedThreadPool();
        for (int i = 0; i < count; i++)
            //一个EvenGenerator被多个线程修改
            ec.execute(new EvenChecker(gp, i));//同一个gp
        ec.shutdown();
    }
    public static void test(IntGenerator gp){
        test(gp,10);
    }
}
abstract class IntGenerator{
    private volatile boolean canceled = false;
    public abstract int next();
    public void cancel(){
        canceled=true;
    }
    public boolean isCanceled(){
        return canceled;
    }
}
class EvenGenerator extends IntGenerator{
    private int currentEvenValue=0;
    @Override
    public /*synchronized*/ int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }
    public static void main(String...arg){
        EvenChecker.test(new EvenGenerator());
    }
}

解决方法
使用synchronized
第一个进入next()的获得锁,操作完之后才会把锁解开,其他线程才会开始执行next();

@Override
    public synchronized int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }

使用lock()加锁
lock(),unlock(),tryLock();

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//使用Lock对象
public class MutexEvenGenerator extends IntGenerator {
    private int currentEvenValue = 0;
    private Lock lock = new ReentrantLock();
    @Override
    public int next() {
        lock.lock();//加锁
        //使用lock更利于异常的处理
        try{
            ++currentEvenValue;
            Thread.yield();//无效
            ++currentEvenValue;
            //return语句写在try语句,最后再去执行unlock(),防止unlock过早执行
            return currentEvenValue;
        }finally{
            lock.unlock();
        }
    }
    public static void main(String...atf){
        EvenChecker.test(new MutexEvenGenerator());
    }
}

trylock()会尝试获得一个锁,获取失败后返回false 成功返回true
tryLock(time); 在time之内尝试得到,得不到返回false

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class AttemptLocking {
    //重入锁??
    private ReentrantLock lock = new ReentrantLock();
    //无延迟
    public void untimed(){
        //tryLock()尝试得到锁,成功返回true,失败返回false,不会阻塞
        boolean capture = lock.tryLock();
        try{
            System.out.println("tryLock() "+capture);
        }finally{
            if(capture){
                //解锁
                lock.unlock();
            }
        }
    }
    //延迟
    public void timed(){
        boolean captured = false;
        try{
            //拿不到锁会等2秒,仍然拿不到返回false,拿到则返回true
            captured = lock.tryLock(2, TimeUnit.SECONDS);

        }catch (InterruptedException i){
            throw new RuntimeException(i);
        }
        try{
            System.out.println("tryLock(2, TimeUnit.SECONDS) "+captured);
        }finally {
            if(captured)
                lock.unlock();
        }
    }
    public static void main(String...args){
        final AttemptLocking al = new AttemptLocking();
        new Thread(){
            {setDaemon(true);
                setPriority(MAX_PRIORITY);}//初始化
            @Override
            public void run() {
                al.lock.lock();
                System.out.println("acquired");
            }
        }.start();
        al.untimed();
        al.timed();
        al.untimed();
        al.timed();
        /*  可能的结果
            acquired
            tryLock() false
            tryLock(2, TimeUnit.SECONDS) false
            tryLock() false
            tryLock(2, TimeUnit.SECONDS) false
        */
    }
}

注意
所有有关访问数据的操作必须写synchronized
下面是原子性的样例

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
//原子性变量类
//使用AtomicInteger,使用2次i.addAndGet(1),和i++;i++;一致
//使用AtomicInteger,使用i.addAndGet(2),正常
//只把evenIncrement()设为synchronized,getVal()不设,仍然读到奇数值
//把evenIncrement(),getVal()设为synchronized,正常
//i+=2 不是原子性的操作,结果不确定
//1. 使用i.addAndGet(2)
//2. 所有访问数据的函数使用synchronized,此时可以使用i++;i++;
public class AtomicIntegerTest implements Runnable{
    private AtomicInteger i = new AtomicInteger(0);
    //private int i = 0;
    public int /*synchronized*/ getValue(){
        return i.get();
        //return i;
    }
    private /*synchronized*/ void evenIncrement(){
        i.addAndGet(2);//+=2
        //i++;i++;
        //i+=2;
    }

    @Override
    public void run() {
        while(true){
            evenIncrement();
        }
    }
    public static void main(String...args){
        //在指定时间后执行一次 计时器
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.err.println("Aborting");
                System.exit(0);
            }
        },5000);//Terminate after 5 seconds
        ExecutorService ece = Executors.newCachedThreadPool();
        AtomicIntegerTest ait = new AtomicIntegerTest();
        ece.execute(ait);
        while(true){
            int value = ait.getValue();
            if(value%2!=0) {
                System.out.println(value);
                System.exit(0);
            }
        }
    }
}

(持续学习,未完待续)

发布了23 篇原创文章 · 获赞 4 · 访问量 841

猜你喜欢

转载自blog.csdn.net/qq_43656529/article/details/101978569