多线程

多线程的话,其实只要学了点儿JAVA的应该都是知道创建的三种方式,之前也是没有仔细对比和思考过,为啥是这样,然后经过一次面试之后,就发现自己在多线程这方面太弱了,所以就来补一补:
梳理一下自己多线程的知识盲区和线程池相关的,主要是自己能理解的更清楚些,所以内容全部手敲,代码也全部手敲,eclipse下实现

创建线程的三种方式

1.继承Thread类重写run方法
package crazyJava;
/*
 * 通过继承Thread类重写run方法来实现的多线程
 */
public class FirstThread extends Thread {

    private int i;
    
    public void run() {
    
        for(;i<100;i++) {
            System.out.println(getName()+" "+i);
        }
    }
    
    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20) {
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }

}

2.实现Runnable接口写run方法
package crazyJava;
//通过实现runnable接口来实现的多线程
public class SecondThread implements Runnable{

    private int i;
    
    public void run() {
        for(;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    
    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20) {
                SecondThread st=new SecondThread();
                new Thread(st,"新线程1").start();
                new Thread(st,"新线程2").start();
            }
        }
    }

}

3.实现Callable接口写call方法,交由FutureTask对象作为Target
package crazyJava;

import java.util.concurrent.*;

public class ThirdThread {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        ThirdThread rt=new ThirdThread();
        
        FutureTask<Integer> task=new FutureTask<Integer>((Callable<Integer>)()->{
            int i=0;
            for(;i<100;i++) {
                System.out.println(Thread.currentThread().getName()+" "+" 的循环变量i的值:"+i);
            }
            return i;
        });
        
        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值:"+i);
            if(i==20) {
                new Thread(task,"有返回值的线程").start();
            }
        }
        
        try {
            System.out.println("子线程的返回值:"+task.get());
        }catch(Exception ex) {
            ex.printStackTrace();
        }
    }

}

上面是通过Lambda表达式来实现的Callable对象

package crazyJava;

import java.util.concurrent.*;

public class ThirdThread2 implements Callable{

    private int i;
    
    @Override
    public Object call() throws Exception {
        // TODO 自动生成的方法存根
        for(;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+"的内循环变量i: "+i);
        }
        return i;
    }
    
    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        ThirdThread2 rt=new ThirdThread2();
        FutureTask<Integer> task=new FutureTask<Integer>(rt);
        //ThirdThread2 rt2=new ThirdThread2();
        FutureTask<Integer> task2=new FutureTask<Integer>(rt);
        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+"的内循环变量i: "+i);
            if(i==20) {
                new Thread(task,"带有返回值的线程1").start();
                new Thread(task2,"带有返回值的线程2").start();
            }
        }
        try {
            System.out.println("两个线程的返回结果的和"+(task.get()+task2.get()));
        }catch(Exception ex) {
            ex.printStackTrace();
        }
    }
    
}


线程同步通信

这里其实还是主要在于理解,因为涉及到通信以及同步,也就是一定要共享资源了,所以必须要进行线程同步实现安全访问共享资源,插一句,也就是为什么和ThreadLocal局部变量的性质完全不同的所在,因为二者解决的本质问题就不同,所以不要混淆。因为锁的原因,这些线程并不是并行在跑的,想要并行的化,也可以理解下ForkJoinPool,一个挺厉害的线程池。
一个习题,写两个线程,其中一个线程打印1-52,另一个线程打印A-Z,打印顺序应该是12A34B...5152Z。如下图所示结果:


xt1601.jpg

这个题可以很好的练习线程的创建、通信、同步。三种基本的创建方式+两种常用的线程通信方式。当然线程的通信方式有很多其他的方式比如使用volatile、使用JUC包里的atom包里的atomInteger、使用BlockingQueue等等。这里主要用synchronized+flag+flag.wait+flag.notifyAll和lock+condition+awit+signalAll来实现。

1.继承Thread类实现线程+Synchronized通信
package crazyJava;

//打印数字的线程
class thread1 extends Thread{

    private comflag flag;
    public thread1(String name,comflag flag) {
        super(name);
        this.setFlag(flag);
    }
    
    public void run() {
        for (int i = 0; i < 52; i += 2) {
            synchronized (flag) {
                try {
                    while (flag.flag != 0)
                        flag.wait();
                    System.out.print((i + 1) + "" + (i + 2));
                    flag.flag = 1;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }

            }

        }


    }

    public comflag getFlag() {
        return flag;
    }
    
    public void setFlag(comflag flag) {
        this.flag = flag;
    }
}

//打印字母的线程
class thread2 extends Thread{

    private comflag flag;
    
    public thread2(String name,comflag flag) {
        super(name);
        this.setFlag(flag);
    }
    
    public void run() {
        for (int i = 0; i < 26; i++) {
            synchronized (flag) {
                try {
                    while (flag.flag != 1)
                        flag.wait();
                    char temp = (char) ('A' + i);
                    System.out.print(temp);
                    flag.flag = 0;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }

    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
    
}

class comflag{
    int flag=0;
}

public class Xiti_16_01 {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        comflag com=new comflag();
        thread1 th1=new thread1("甲",com);
        thread2 th2=new thread2("乙",com);
        th1.start();
        th2.start();
    }

}
2.Runnable接口实现多线程+synchronized
package crazyJava;

class myrun1 implements Runnable{

    private comflag flag;
    
    public myrun1(comflag flag){
        this.setFlag(flag);
    }
    
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int i = 0; i < 52; i += 2) {
            synchronized (flag) {
                try {
                    while (flag.flag != 0)
                        flag.wait();
                    System.out.print((i + 1) + "" + (i + 2));
                    flag.flag = 1;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }

            }

        }
    }

    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
    
}

class myrun2 implements Runnable{

    private comflag flag;
    
    public myrun2(comflag flag){
        this.setFlag(flag);
    }
    
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int i = 0; i < 26; i++) {
            synchronized (flag) {
                try {
                    while (flag.flag != 1)
                        flag.wait();
                    char temp = (char) ('A' + i);
                    System.out.print(temp);
                    flag.flag = 0;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
    }

    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
    
}

public class Xiti_16_011 {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        comflag flag=new comflag();
        new Thread(new myrun1(flag)).start();
        new Thread(new myrun2(flag)).start();
    }

}
3.Callable+FutureTask+synchronized

不难发现的是,越来越复杂了,其实也是越来越有用,因为FutureTask对象是带返回值的,因此我们可以通过task对象来调用get()方法拿到返回值,而参数的化也是可以我们来指定或者在结果的时候强制转换一下。这也是当时为啥可以用该方法来分别计算多个任务再求和,我们可以用在主线程里用task.get()获取返回值求和,只是这个时候当子线程的任务没有完成的时候,主线程因为要获取返回值是会被阻塞挂起来的。

package crazyJava;

import java.util.concurrent.*;

class mycal1 implements Callable<Object>{

    private comflag flag;
    
    public mycal1(comflag flag) {   
        this.setFlag(flag);
    }
    
    @Override
    public Object call() throws Exception {
        // TODO 自动生成的方法存根
        for (int i = 0; i < 52; i += 2) {
            synchronized (flag) {
                try {
                    while (flag.flag != 0)
                        flag.wait();
                    System.out.print((i + 1) + "" + (i + 2));
                    flag.flag = 1;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }

            }

        }
        return null;
    }

    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
    
}

class mycal2 implements Callable<Object>{

    private comflag flag;
    
    public mycal2(comflag flag) {
    
        this.setFlag(flag);
    }
    
    @Override
    public Object call() throws Exception {
        // TODO 自动生成的方法存根
        for (int i = 0; i < 26; i++) {
            synchronized (flag) {
                try {
                    while (flag.flag != 1)
                        flag.wait();
                    char temp = (char) ('A' + i);
                    System.out.print(temp);
                    flag.flag = 0;
                    flag.notifyAll();
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
    
}

public class Xiti_16_012 {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        comflag flag=new comflag();
        FutureTask<Object> task=new FutureTask<>(new mycal1(flag));
        FutureTask<Object> task2=new FutureTask<>(new mycal2(flag));
        new Thread(task,"甲").start();
        new Thread(task2,"乙").start();
    }

}

4.继承Thread类+Lock+Condition+flag
package crazyJava;

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

class thread_c1 extends Thread{

    private comflag_c flag;
    public thread_c1(comflag_c flag) {

        this.flag=flag;
    }

    public void run() {
        for (int i = 0; i < 52; i += 2) {
            flag.lock.lock();
            try {
                while (flag.flag!= 0)
                    flag.con.await();
                System.out.print((i + 1) + "" + (i + 2));
                flag.flag = 1;
                flag.con.signalAll();
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }finally {
                flag.lock.unlock();
            }

        }
    }
}

class thread_c2 extends Thread{

    private comflag_c flag;
    
    public thread_c2(comflag_c flag) {

        this.flag=flag;
    }

    public void run() {
        for (int i = 0; i < 26; i++) {
            flag.lock.lock();
            try {
                while (flag.flag != 1)
                    flag.con.await();
                char temp = (char) ('A' + i);
                System.out.print(temp);
                flag.flag = 0;
                flag.con.signalAll();
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }finally {
                flag.lock.unlock();
            }
        }
    }

}

class comflag_c{    
    Lock lock=new ReentrantLock();
    Condition con=lock.newCondition();
    int flag=0;
}

public class Xiti_16_01c1 {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        comflag_c flag=new comflag_c();
        new thread_c1(flag).start();
        new thread_c2(flag).start();
    }

}
5.BlockingQueue+flag
package crazyJava;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;


class Producer1 extends Thread{
    private BlockingQueue<Integer> bq;
    private comflag flag;
    public Producer1(BlockingQueue<Integer> bq,comflag flag) {
        this.bq=bq;
        this.setFlag(flag);
    }
    
    public void run() {
        for (int i = 0; i < 52; i += 2) {
            produce(i);
        }
    }

    @SuppressWarnings("finally")
    public int produce(int i) {
        try {
            bq.put(i);
            while(flag.flag!=0) {System.out.print('-');};//如果旗标指示现在应该consumer线程执行,则当前
                                   //循环等待,知道阻塞挂起当前线程
            System.out.print((i + 1) + "" + (i + 2));
            flag.flag=1;
        } catch(InterruptedException e) {
            System.out.println(e);
        }finally {
            return 1;
        }
    }
    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
}

class Consumer1 extends Thread{
    private BlockingQueue<Integer> bq;
    private comflag flag;
    public Consumer1(BlockingQueue<Integer> bq,comflag flag) {
        this.bq=bq;
        this.setFlag(flag);
    }
    
    public void run() {
        for (int i = 0; i < 26; i++) {
            consume(i);
        }

    }

    public void consume(int i) {
        try {
            bq.take();//这里如果不可取是会阻塞当前线程,但是一旦可取且取了之后,生产者线程也唤醒了
                      //因此如果不加下面的while循环判断旗标,则有可能会出现错乱的执行,因为生产者可以抢占CPU,同理
                      //生产者那里也需要一个循环判断旗标的过程,果然还是得自己亲自手敲一遍,尽信书,则不如无书
            while(flag.flag!=1) {System.out.print('-');}
            char temp = (char) ('A' + i);               
            System.out.print(temp);
            flag.flag=0;
        } catch(InterruptedException e) {
            System.out.println(e);
        }
    }
    public comflag getFlag() {
        return flag;
    }

    public void setFlag(comflag flag) {
        this.flag = flag;
    }
}

public class Xiti_16_01_BQ {

    public static void main(String[] args) {
        // TODO 自动生成的方法存根

        comflag flag=new comflag();
        BlockingQueue<Integer> bq=new ArrayBlockingQueue<>(1);
        new Producer1(bq,flag).start();
        new Consumer1(bq,flag).start();
    }
}

我真的有点儿服了现在写书的人,感觉并不完全靠谱,所以这个图也让我更明白了旗标flag的作用,以及如何使用旗标,也更加理解了所谓的锁,锁类以及阻塞队列的作用,其实大家的原理都差不多,只是具体实现和用法不一样。


image.png
多线程同步通信总结

在用synchronized对象锁的时候,我们做线程的同步通信的时候,两个线程共享了一个flag对象,而对象的flag.wait()方法阻塞当前线程,并释放当前线程持有的对象锁,因此可以实现线程的切换;falg.notify()和notifyAll()唤醒被flag对象锁阻塞的线程们,区别在与flag.notify()唤醒一个,而flag.notifyAll()唤醒所有。在使用Lock锁类的时候,原理一样的,flag实现线程切换或者说通信,只是这个时候用的锁对象来实现锁,用condition对象来实现线程的阻塞和唤醒。在用BlockingQueue的时候,也是一样的,flag实现切换,不同的是,BlockingQueue是一个同步工具的队列,里面有一个生产者和消费者线程,put和take来实现的放入和取出,是会阻塞线程的。上面的代码也具体的反应了这段话,可以理解一些了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容