多线程学习-2.线程共享和协作

一、线程间的共享

synchronized内置锁

对象锁,锁的是类的对象实例。
类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。

class Sync {  
  
    public void test() {  
        synchronized (Sync.class) {  
            System.out.println("test开始..");  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            System.out.println("test结束..");  
        }  
    }  
}  
  
class MyThread extends Thread {  
  
    public void run() {  
        Sync sync = new Sync();  
        sync.test();  
    }  
}  
  
public class Main {  
  
    public static void main(String[] args) {  
        for (int i = 0; i < 3; i++) {  
            Thread thread = new MyThread();  
            thread.start();  
        }  
    }  
}  

volatile关键字

适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。

package com.enjoy.demo.p1.ch1.class1;

/**
 * @Author: BillYu
 * volatile应用场景:一个线程写多个线程读
 * @Description:演示volatile无法提供操作的原子性
 * @Date: Created in 15:44 2019-02-20.
 */
public class VolatileUnsafe {
    private static class VolatileVar implements Runnable{
        private volatile int a = 0;
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            a = a+1;
            System.out.println(threadName+":===="+a);
            SleepTools.ms(100);
            a = a +1;
            System.out.println(threadName+":===="+a);

        }
    }

    public static void main(String[] args) {
        VolatileVar v = new VolatileVar();
        Thread t1 = new Thread(v);
        Thread t2 = new Thread(v);
        Thread t3 = new Thread(v);
        Thread t4 = new Thread(v);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}

ThreadLocal

线程变量。可以理解为是个map,类型 Map<Thread,Integer>

package com.enjoy.demo.p1.ch1.class1;

/**
 * @Author: BillYu
 * @Description:ThreadLocal使用
 * 每个线程拥有自己线程变量副本,保证线程之间变量不会冲突
 * 注意:尽量存储一些占用内存比较小的变量,否则会占用太多内存
 * @Date: Created in 15:54 2019-02-20.
 */
public class UseThreadLocal {

    static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    /**
     * 运行3个线程
     */
    public void startThreadArray(){
        Thread [] runs = new Thread[3];
        for (int i =0;i<runs.length;i++){
            runs[i] = new Thread(new TestThread(i));
        }
        for (int i = 0;i<runs.length;i++){
            runs[i].start();
        }
    }

    /**
     * 类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会相互影响
     */
    public static class TestThread implements Runnable{
        int id;
        public TestThread(int id){
            this.id = id;
        }
        @Override
        public void run(){
            System.out.println(Thread.currentThread().getName()+":start");
            //获得变量的值
            Integer s = threadLocal.get();
            s = s + id;
            threadLocal.set(s);
            System.out.println(Thread.currentThread().getName()+ threadLocal.get());
        }
    }


    public static void main(String[] args) {
        UseThreadLocal test = new UseThreadLocal();
        test.startThreadArray();
    }


}

线程间协作

轮询:难以保证及时性,资源开销很大,

等待和通知

wait() 对象上的方法
notify/notifyAll 对象上的方法

package com.enjoy.demo.p1.ch1.class2;

/**
 * @Author: BillYu
 * @Description: wait notify notifyAll
 * @Date: Created in 11:19 2019-02-25.
 */
public class WaitNotify {
    private Object lock = new Object();
    public class WaitThread extends Thread{

        @Override
        public void run() {
            try {
                synchronized (lock){
                    System.out.println("begin wait() ThreadName = "+Thread.currentThread().getName());
                    lock.wait();
                    //当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
                    System.out.println("end wait() ThreadName = "+Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public class NotifyThread extends Thread{

        @Override
        public void run() {
            try {
                synchronized (lock){
                    System.out.println("begin notify() threadName = "
                            + Thread.currentThread().getName()+" time = "
                            + System.currentTimeMillis());
//                    lock.notify();
                    lock.notifyAll();
                    // 当执行notify/notifyAll方法时,会唤醒一个处于等待该 对象锁 的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。
                    Thread.sleep(5000);
                    System.out.println(" end notify() ThreadName = "+ Thread.currentThread().getName()+" time ="
                            +System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        WaitNotify test = new WaitNotify();
//        test.new WaitThread().start();
        WaitThread waitThread1 = test.new WaitThread();
        waitThread1.start();
        WaitThread waitThread2 = test.new WaitThread();
        waitThread2.start();
        WaitThread waitThread3 = test.new WaitThread();
        waitThread3.start();
        test.new NotifyThread().start();


        /**
         * 实验测试结果:
         * notify唤醒顺序:唤醒最早执行wait()的线程
         * 处于wait状态的线程会被虚拟机放在一个队列中,第一个放进去的线程会被notify先唤醒
         *
         * notifyAll唤醒顺序:最晚执行wait()的线程最早被唤醒
         * 应该尽量使用notifyAll, 因为使用notify有可能发生信号丢失的情况,被同样等待通知的线程拦截
         */

    }
}

等待和通知的标准范式

等待方:
1、 获取对象的锁;
2、 循环里判断条件是否满足,不满足调用wait方法,
3、 条件满足执行业务逻辑
通知方来说
1、 获取对象的锁;
2、 改变条件
3、 通知所有等待在对象的线程

notify和notifyAll应该用谁?

应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况

join()方法

线程A,执行了线程B的join方法,线程A必须要等待B执行完成了以后,线程A才能继续自己的工作

package com.enjoy.demo.p1.ch1.class2;

import com.enjoy.demo.p1.ch1.class1.SleepTools;

/**
 * @Author: BillYu
 * @Description:演示join方法的使用
 * @Date: Created in 15:42 2019-02-21.
 */
public class UseJoin {
    static class JumpQueue implements Runnable{
        /**
         * 用来插队的线程
         */
        private Thread thread;

        public JumpQueue(Thread thread){
            this.thread = thread;
        }

        @Override
        public void run(){
            try{
                thread.join();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" terminated.");
        }
    }

    public static void main(String[] args) {
        //拿到主线程
        Thread previous = Thread.currentThread();
        for (int i =0 ;i<10;i++) {
            Thread thread = new Thread(
                    new JumpQueue(previous),String.valueOf(i)
            );
            System.out.println(previous.getName()+" jump a queue the thread:"+ thread.getName());
            thread.start();
            previous = thread;
        }
        //让祝线程休眠2秒
        SleepTools.second(2);
        System.out.println(Thread.currentThread().getName()+" terminate");

    }

}

调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?

线程在执行yield()以后,持有的锁是不释放的
sleep()方法被调用以后,持有的锁是不释放的
调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的

package com.enjoy.demo.p1.ch1.class2;

/**
 * @Author: BillYu
 * @Description: 验证线程休眠不释放锁
 * @Date: Created in 10:26 2019-02-25.
 */
public class SleepLock {
    private Object lock = new Object();

    public static void main(String[] args) {
        SleepLock sleepTest = new SleepLock();
        Thread threadA = sleepTest.new ThreadSleep();
        threadA.setName("ThreadSleep");
        Thread threadB = sleepTest.new ThreadNotSleep();
        threadB.setName("ThreadNotSleep");
        threadA.start();
        try {
            Thread.sleep(1000);
            System.out.println("Main slept");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadB.start();
    }

    /**
     * 休眠线程
     */
    private class ThreadSleep extends Thread{
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println("threadName"+ "will take the lock");
            //alt shift z
            try {
                synchronized (lock){
                    System.out.println(threadName+" taking the lock");
                    Thread.sleep(5000);
                    System.out.println("Finish the work : "+threadName);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 不休眠线程
     */
    private class ThreadNotSleep extends Thread{
        @Override
        public void run(){
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+ " will take the lock time = "+System.currentTimeMillis());
            synchronized (lock){
                System.out.println(threadName+" taking the lock time = "+System.currentTimeMillis());
                System.out.println("Finish the work: "+threadName);
            }
        }
    }




}

wait、notify对应的生产者、消费者问题

模拟场景:产品数量大于0时停止生产,通知消费者消费,产品数量为0时停止消费,通知生产者生产

package com.enjoy.demo.p1.ch1.class2;

/**
 * @Author: BillYu
 * @Description:生产者消费者
 * @Date: Created in 13:33 2019-02-25.
 */
public class ProduceConsume {

    private Object lock = new Object();

    private Integer productNum = 0;

    public class ProduceThread extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (productNum > 0) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    productNum = productNum + 5;
                    System.out.println("生产5个产品,当前产品数量: " + productNum);
                    lock.notifyAll();
                }
            }
        }
    }

    public class ConsumeThread extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (productNum == 0) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    productNum = productNum - 1;
                    System.out.println(Thread.currentThread()+"消费1个产品,当前产品数量:" + productNum);
                    lock.notifyAll();
                }
            }
        }
    }


    public static void main(String[] args) {
        ProduceConsume main = new ProduceConsume();
        ConsumeThread consume1 = main.new ConsumeThread();
        ConsumeThread consume2 = main.new ConsumeThread();
        consume1.start();
        consume2.start();
        ProduceThread produce1 = main.new ProduceThread();
        produce1.start();

    }


}
已有 2 条评论
  1. typecycle

    buy cialis delived next day Cialis Buy Synthroid 175 cialis for sale Zithromax Effects On Birth Control

    typecycle June 12th, 2020 at 08:53 pm回复
  2. unirtiern

    comprar cialis campinas invignnada https://bbuycialisss.com/# - Cialis usadaySaug achat cialis securise gorgobeAmomy Cialis popledepShom Buy 125 Mcg Levothyroxine Without Rx

    unirtiern July 15th, 2020 at 03:30 am回复
发表新评论