多线程经典题目

多线程编程

目录

work1

package cn.tedu.work;



public class ThreadWork1 {

    public static void main(String[] args) {
        RunnableThread1 target_a=new RunnableThread1();
        Thread thread_a = new Thread(target_a, "a");
        Thread thread_b = new Thread(target_a, "b");
        Thread thread_c = new Thread(target_a, "c");
        try {
            thread_a.start();
            thread_a.join();
            thread_b.start();
            thread_b.join();
            thread_c.start();
            thread_c.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class RunnableThread1 implements Runnable {

    public void run() {
        Thread thread=Thread.currentThread();
        System.out.println("当前线程为"+thread.getName());
        System.out.println("线程"+thread.getName()+"休眠2000ms");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程"+thread.getName()+"执行结束\n");
    }

}

work2

package cn.tedu.work;


public class ThreadWork2 {


    /**
     * 使用
     * */
    public static void main(String[] args) {
        RunnableThread2 target1 = new RunnableThread2();
        Thread threada = new Thread(target1, "a");
        Thread threadb = new Thread(target1, "b");
        threada.start();
        threadb.start();
    }


}

class RunnableThread2 implements Runnable {

    public static int nub = 0;
    public static long th_id = 0;
    Object ob = new Object();

    public void run() {

        while (true) {
            synchronized (ob) {
                long cunnt_id = Thread.currentThread().getId();
                if (cunnt_id != th_id && nub <= 100) {
                    System.out.println(Thread.currentThread().getName() + ": " + (nub));
                    th_id = cunnt_id;
                    nub++;
                } else if (nub > 100) {
                    break;
                }
            }

        }

    }
}

work3

package cn.tedu.work;

import java.security.Principal;

public class ThreadWork3 {

    /**
     * 写两个线程,一个线程打印1~ 52,另一个线程打印A~Z,打印顺序是12A34B...5152Z
     * */
    public static void main(String[] args) {
        Thread thread1=new Thread(new PrintNub(),"nub");
        Thread thread2 = new Thread(new PrintChar(), "char");
        thread1.start();
        thread2.start();
    }

}



class PrintNub implements Runnable{


    public void run() {
        int nub=1;
        while (true){
            synchronized (ThreadWork3.class){
                if(nub<52){
                    System.out.println(nub+" "+(nub+1));
                    nub+=2;
                }else if(nub>=52){
                    ThreadWork3.class.notifyAll();
                    break;
                }

                ThreadWork3.class.notifyAll();
                try {
                    ThreadWork3.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

class PrintChar implements Runnable{

    public static Object ob=new Object();

    public void run() {


        char a='A';
        while (true){
            synchronized (ThreadWork3.class){
                if(a<='Z'){
                    System.out.println(a);
                    a+=1;
                }else if(a>'Z'){
                    ThreadWork3.class.notify();
                    break;
                }
                ThreadWork3.class.notifyAll();
                try {
                    ThreadWork3.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

        }

    }
}

work4

package cn.tedu.work;

public class ThreadWork4 {
    /**
     * 编写一个程序,启动三个线程,三个线程的ID分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC...
     * */
    public static void main(String[] args) {
        ThreadA target=new ThreadA();
        Thread thread1 = new Thread(target, "A");
        Thread thread2 = new Thread(target, "C");
        Thread thread3 = new Thread(target, "B");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}


class ThreadA implements Runnable {

    public  volatile char t_char='A';

    public void run() {
        for (int i = 0; i < 5; ) {
            Thread thread = Thread.currentThread();
            char ch = thread.getName().charAt(0);
            if (ch==t_char) {
                System.out.printf(Thread.currentThread().getName());
                i++;
                t_char= (char) ('A'+(t_char-'A'+1)%3);
            }
        }
    }
}





work5

package cn.tedu.work;


public class ThreadWork5 {

    /**
     * 编写10个线程,第一个线程从1加到10,
     * 第二个线程从11加20…第十个线程从91加到100,
     * 最后再把10个线程结果相加。
     * */
    public static void main(String[] args) {

        for(int i=0;i<10;i++){
            ThreadAdd target = new ThreadAdd(i);
            new Thread(target).start();
        }
    }
}

class ThreadAdd implements Runnable{
//    WriteLock lock = new WriteLock();
    public static int sum;
    public int time;

    public ThreadAdd(int time) {
        this.time = time;
    }

    public void run() {
        int t_sum=0;
        for(int i=1;i<=10;i++) {
            t_sum += (time * 10)+i;
        }
//        System.out.println(t_sum);
        sum+=t_sum;
        if(time==9){
            System.out.println(sum);
        }
    }
}

work6

package cn.tedu.work;

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

public class ThreadWork6 {


    /**
     * 三个窗口同时卖票
     */
    public static void main(String[] args) {

        TicketThread target = new TicketThread();
        Thread thread1 = new Thread(target, "1");
        Thread thread2 = new Thread(target, "2");
        Thread thread3 = new Thread(target, "3");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class TicketThread implements Runnable {

    public int ticket_nub = 100;
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    static Lock lock = rwl.writeLock();

    public void run() {


        while (true) {
            lock.lock();
            try {
                if (ticket_nub > 0) {
                    System.out.println("您的票号为:" + ticket_nub + "");
                    System.out.println("窗口: " + Thread.currentThread().getName());
                    Thread.sleep(100);
                    ticket_nub--;
                } else if (ticket_nub <= 0) {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }
        System.out.println("结束线程:" + Thread.currentThread().getName());
    }
}

work7

https://zhuanlan.zhihu.com/p/65229222

package cn.tedu.work;


import java.awt.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadWork7 {

    /**
     * 7、 生产者消费者
     * 7.1 synchronized方式
     * 7.2 ReentrantLock方式 (可以保证顺序)
     * 7.3 BlockingQueue方式
     */
    public static void main(String[] args) {

//        new BufferShelf().synchronizedMode();
        new BufferShelf().ReentrantLockMode();
//        new BufferShelf().BlockingQueueMode();
    }


}


enum Goodstype {
    烤肉,
    茄子,
    火锅,
    油条
}


class BufferShelf {
    //    private static final Object L = ;
    public static LinkedList<Map<String, String>> goodsBuffer = new LinkedList<>();
    static int bufferNub = 2;
    static long starttinme;

    public void push() {

        Random rand = new Random();
        if (goodsBuffer.size() < bufferNub) {
            HashMap<String, String> map = new HashMap<String, String>();
            int goods_ind = rand.nextInt(Goodstype.values().length);
            map.put("name", Goodstype.values()[goods_ind].toString());
            goodsBuffer.push(map);
            System.out.println(Thread.currentThread().getName() + "生产: " +map.get("name")+"  buffer"+ goodsBuffer);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public void pop() {
        if (goodsBuffer.size() > 0) {
            Map<String, String> pop = goodsBuffer.pop();
//          System.out.println("消费:" + pop);
            System.out.println(Thread.currentThread().getName() + "消费:"+pop.get("name") +"  buffer"+ goodsBuffer);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public void push_s(){
        synchronized (BufferShelf.class){
            push();
        }
    }
    public void pop_s(){
        synchronized (BufferShelf.class){
            pop();
        }
    }

    public void push_l(){
        lock.lock();
        try {
            push();
        } finally {
            lock.unlock();
        }
    }

    public void pop_l(){
        lock.lock();
        try {
            pop();
        } finally {
            lock.unlock();
        }
    }


    public void run(Runnable producer_t, Runnable consumer_t) {
        Thread producer = new Thread(producer_t, "P1");
        Thread producer2 = new Thread(producer_t, "P2");
        Thread producer3 = new Thread(producer_t, "P3");
        Thread consumer = new Thread(consumer_t, "C1");
        Thread consumer2 = new Thread(consumer_t, "C2");
        Thread consumer3 = new Thread(consumer_t, "C3");
        producer.start();
        producer2.start();
        producer3.start();
        consumer.start();
        consumer2.start();
        consumer3.start();
    }



    public void synchronizedMode() {
        starttinme = System.currentTimeMillis();
        Runnable producer_t = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    push_s();
                    if (System.currentTimeMillis() - starttinme > 10000) {
                        break;
                    }
                }
            }
        };
        Runnable consumer_t = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    pop_s();
                    if (System.currentTimeMillis() - starttinme > 10000) {
//                        System.out.println(goodsBuffer);
                        break;
                    }
                }
            }
        };
        run(producer_t, consumer_t);
    }





    static Lock lock = new ReentrantLock();

    public void ReentrantLockMode() {
        starttinme = System.currentTimeMillis();
        Runnable producer_t = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    push_l();
                    if (System.currentTimeMillis() - starttinme > 2000) {
//                        System.out.println(goodsBuffer);
                        break;
                    }
                }
            }
        };
        Runnable consumer_t = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    pop_l();
                    if (System.currentTimeMillis() - starttinme > 10000) {
                        break;
                    }

                }
            }
        };
        run(producer_t, consumer_t);
    }

//    static BlockingQueue<Map<String, String>> buff = new LinkedBlockingDeque<>(bufferNub);

    static BlockingQueue< String> buff = new LinkedBlockingDeque<>(bufferNub);

    /**
     *  buff BlockingQueueMode 下的缓冲区  缓冲器的大小为bufferNub
     *
     *
     * */
    public void BlockingQueueMode() {
        starttinme = System.currentTimeMillis();
        Runnable producer_t = new Runnable() {

            @Override
            public void run() {
                while (true) {
                    Random rand = new Random();
                    HashMap<String, String> map = new HashMap<String, String>();
                    int goods_ind = rand.nextInt(Goodstype.values().length);
                    map.put("name", Goodstype.values()[goods_ind].toString());
                    try {
//                        buff.put(map);
                        String aput=Goodstype.values()[goods_ind].toString();

                        buff.put(aput);
                        boolean bl = true;
//                        bl = buff.offer(map, 10, TimeUnit.MILLISECONDS);
                        if (bl) {
//                            System.out.println(Thread.currentThread().getName() + "生产:" + aput+ " buff="+buff);
                            System.out.println(Thread.currentThread().getName() + "生产:" + aput);
                        }
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (System.currentTimeMillis() - starttinme > 2000) {
                        break;
                    }
                }

            }
        };

        Runnable consumer_t = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
//                        Map<String, String> pop = buff.take();
//                        Map<String, String> pop = buff.poll(10, TimeUnit.MILLISECONDS);
                        String pop = buff.poll(10, TimeUnit.MILLISECONDS);
//                        System.out.println(Thread.currentThread().getName() + "消费 " + pop+"  buff="+ buff);
                        System.out.println(Thread.currentThread().getName() + "消费 " + pop);
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (System.currentTimeMillis() - starttinme > 2000) {
//                        System.out.println(goodsBuffer);
                        break;
                    }
                }

            }
        };
        run(producer_t, consumer_t);

    }


}






work8

package cn.tedu.work;


import java.util.concurrent.locks.LockSupport;

public class ThreadWork8 {

    static Thread thread1;
    static Thread thread2;
    /**
     * 交替打印两个数组
     * */
    public static void main(String[] args) {
        int[] nubs=new int[10];
        char[] chars=new char[10];
        for(int i=0;i<10;i++){
            nubs[i]=i;
            chars[i]= (char) (i+'a');
        }

      thread1=new Thread( ()->{
            for(char ach:chars){
                System.out.printf(ach+" ");

                LockSupport.unpark(thread2);
                LockSupport.park();
            }
        });
       thread2=new Thread(()->{
            for(int nub:nubs){
                System.out.printf(nub+" ");
                LockSupport.unpark(thread1);
                LockSupport.park();

            }
        });
       thread1.start();
       thread2.start();

    }
}



锁的实现

  • synchronized

    锁对象为 this 对象 或者传入参数对象

  • ReentrantLock

    锁对象为Lock

    ReentrantLock lock = new ReentrantLock();
    lock.lock();
    lock.unlock;
    
  • wirterLock / readerLock

    ReadWriteLock 
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
    Lock lock=rwl.readLock();
    Lock lock=rwl.writeLock();
    lock.lock();
    lock.unlock;
    
  • 信号量Semaphore

    通常用于限制同时访问资源的线程数。例如,某段代码只允许最多三个线程进入执行,那么就可以初始化Semaphore为3,每次进入时使用acquire进行减一,退出这段代码时使用release加1,就可以达到控制的目的。

    semaphore针对的是资源的可进入次数,如果可进入次数为1,那么此时可以类比lock的实现,遍又想到了使用reentrantlock的condition实现的另一个变种,一个lock的两个condition,类似一个资源的两个semaphore。代码的写法几乎一样,就不再在这贴出来了。

    //      s1.acquire();  获取信号量 相当于上锁
    // 		s1.release();  释放锁
    
    private static Semaphore s1 = new Semaphore(1);
    private static Semaphore s2 = new Semaphore(0);
    // s1.acquire() //获取锁 
    // s2.acquire(); //得到阻塞  等待G中 
    // s1.acquire() -> s1.run->s2.release()->s1.
    
    static class G extends Thread{
        @Override
        public void run() {
            while (true){
                try {
                    s1.acquire();  //只能有一个线程能够执行下面代码
                    System.out.println("G");
                    s2.release(); //必须在s1打开请时等到G线程释放 s2.才能进行执行 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    static class H extends Thread{
        @Override
        public void run() {
            while (true){
                try {
                    s2.acquire();
                    System.out.println("H");
                    s1.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    }
    
    
    • 锁的实现

    • 阻塞呢

线程间同步

  • volatile 关键字

    static volatile boolean a = true;
    

    volatile能够保证可见性和有序性。

    可见性,被volatile修饰的变量的修改能立刻同步到主存,由于处理器缓存一致性机制,会使其他线程缓存中的数据失效,从而能够保证线程读到的都是最新数据。

    有序性,防止指令重排序,指令重排序在单线程下是没有问题的,因为不会影响最终的结果,只是处理顺序上有不同,但是在多线程情况下,一旦指令进行重排序,会出现一些意料之外的结果。例如单例模式下,单例对象实例化完成之前引用提前暴露。当变量被声明为volatile之后,会在读写间建立happens-before关系,通过添加内存屏障,来禁止指令重排序。

  • 使用静态变量

唤醒 阻塞

  • LockSupport 工具类

    LockSupport是一个线程工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,也可以在任意位置唤醒。

    它的内部其实两类主要的方法:park(停车阻塞线程)和unpark(启动唤醒线程)。

      LockSupport.unpark(a);
      LockSupport.park();
    
  • wait()/notify Object

  • Reentrantlock Condition await/single

    当调用condition.await()方法后会使得当前获取lock的线程进入到等待队列,如果该线程能够从await()方法返回的话一定是该线程获取了与condition相关联的lock。

    调用condition的signal或者signalAll方法可以将等待队列中等待时间最长的节点移动到同步队列中。

    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
      condition.signal(); //唤醒最早进入的
      condition.await(); //加入等待队列
    
    

交替打印方式

  • 方式一:

    • 只有一个线程 进行打印 使用独占锁 或者使用
    • 完成线程的切换
      • 使用静态变量
      • 使用线程阻塞 唤醒
  • 方式二

    • 使用线程同步的变量
  • 只有一个语句或者一个变量 比如基数

    • 使用原子类型

相关blog

消费者生产者

三个线程打印ABC

线程交替打印的几种实现方式

Java中的锁分类

Java Lambda 表达式

Java AtomicInteger的用法

Lambda

Lambda 即是匿名函数

匿名类

原子类型

java里的运算(比如自增)并不是原子性的。

理论锁模型

  • 乐观锁/悲观锁

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

//怎么判断需要更新数据,

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。比如Java里面的同步原语synchronized关键字的实现就是悲观锁。

  • 独享锁/共享锁

独享锁是指该锁一次只能被一个线程所持有。

​ 只能被一个

共享锁是指该锁可被多个线程所持有。

对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。

读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

对于Synchronized而言,当然是独享锁。

  • 互斥锁/读写锁

    上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

    互斥锁在Java中的具体实现就是ReentrantLock。

    读写锁在Java中的具体实现就是ReadWriteLock。

  • 可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

对于Java ReetrantLock而言,从名字就可以看出是一个重入锁,其名字是Re entrant Lock 重新进入锁。

对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。

1 synchronized void setA() throws Exception{
2   Thread.sleep(1000);
3   setB();
4 }
5 
6 synchronized void setB() throws Exception{
7   Thread.sleep(1000);
8 }

上面的代码就是一个可重入锁的一个特点。如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

  • 公平锁/非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

对于Java ReetrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。

对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

  • 分段锁

分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

​ 数组插入的时候判断插入分段,锁掉那个分段,可以实现并行插入

我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

  • 偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。

Java锁底层实现

CAS(Compare and Swap 比较并交换),当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值