多线程学习(第三天)线程间通信

一、volatile 与 synchronized

  java多线程支持每个线程拥有对象的拷贝,这样每个线程内部就是独立的java运行环境。但是这样存在问题,共享内存中的对象或变量,在线程内对其拷贝进行修改后,其他线程读取的数据则为脏数据。

  volatile:作用就是告诉程序,当线程修改拷贝后,需要将修改后的内容同步到内存中,其他线程读取数据时,需要到内存中同步。(缺点:影响效率)

  synchronized:可以修饰方法或代码块,保证多线程的情况,同一时间只允许一个线程执行该代码。

  当一个线程获取到锁之后,其他线程进入同步队列,线程状态编程BLOCKED。当线程执行完成后,会进行释放操作,并唤醒所有等待的线程。

  代码验证:

package com.example.thread.state;

public class MyThread implements Runnable {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread one = new Thread(myThread, "线程1");
        Thread two = new Thread(myThread, "线程2");
        Thread three = new Thread(myThread, "线程3");
        Thread four = new Thread(myThread, "线程4");

        one.start();
        two.start();
        three.start();
        four.start();

        while(true) {
            System.out.println("线程1状态:"+one.getState());
            System.out.println("线程2状态:"+two.getState());
            System.out.println("线程3状态:"+three.getState());
            System.out.println("线程4状态:"+four.getState());
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
        synchronized (this) {
            try {
                System.out.println(Thread.currentThread().getName()+"抢到了锁");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"释放锁");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
Main

  结果状态:

线程1状态:RUNNABLE
线程2状态:RUNNABLE
线程3状态:RUNNABLE
线程4状态:RUNNABLE
线程2抢到了锁
线程1状态:BLOCKED
线程2状态:TIMED_WAITING
线程3状态:BLOCKED
线程4状态:BLOCKED
线程1状态:BLOCKED
线程2状态:TIMED_WAITING
线程3状态:BLOCKED
线程4状态:BLOCKED
线程1状态:BLOCKED
线程2状态:TIMED_WAITING
线程3状态:BLOCKED
线程4状态:BLOCKED
线程2释放锁
线程4抢到了锁
线程1状态:BLOCKED
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TIMED_WAITING
线程1状态:BLOCKED
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TIMED_WAITING
线程1状态:BLOCKED
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TIMED_WAITING
线程1状态:BLOCKED
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TIMED_WAITING
线程4释放锁
线程1抢到了锁
线程1状态:TIMED_WAITING
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TERMINATED
线程1状态:TIMED_WAITING
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TERMINATED
线程1状态:TIMED_WAITING
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TERMINATED
线程1状态:TIMED_WAITING
线程2状态:TERMINATED
线程3状态:BLOCKED
线程4状态:TERMINATED
线程1释放锁
线程3抢到了锁
线程1状态:TERMINATED
线程2状态:TERMINATED
线程3状态:TIMED_WAITING
线程4状态:TERMINATED
线程1状态:TERMINATED
线程2状态:TERMINATED
线程3状态:TIMED_WAITING
线程4状态:TERMINATED
线程1状态:TERMINATED
线程2状态:TERMINATED
线程3状态:TIMED_WAITING
线程4状态:TERMINATED
线程1状态:TERMINATED
线程2状态:TERMINATED
线程3状态:TIMED_WAITING
线程4状态:TERMINATED
线程3释放锁
线程1状态:TERMINATED
线程2状态:TERMINATED
线程3状态:TERMINATED
线程4状态:TERMINATED

 

二、等待/通知机制

  等待(wait):

  wait的使用前提是,线程必须获取当前对象的锁。所以wait的使用必须在synchronized中。

  通知(notify):

  notify唤醒一个等待当前锁的线程。(多个线程等待的时候,随机唤醒一个)。synchronized中使用。

wait和sleep比较
wait:当前线程会释放锁
sleep:不释放锁

  下面一洗车喷漆为例

public class Car {

    /**
     * INIT:初始化
     * WASHED: 洗完
     * COLORED:染完色
     */
    private String state;

    public Car(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

}
package com.example.thread.wait.carworker;

import com.example.thread.SleepUtil;

/**
 * 给车上色
 */
public class CarColor implements Runnable{
    Car car;
    public CarColor(Car c) {
        this.car = c;
    }
    
    @Override
    public void run() {

        synchronized (car) {

            while (true) {

                System.out.println(Thread.currentThread().getName() + "准备上色");

                if ("WASHED".equals(car.getState())) {

                    System.out.println("上色中。。。。。。");
                    SleepUtil.sleep(3000);
                    car.setState("COLORED");
                    car.notify();
                    System.out.println(Thread.currentThread().getName() + "完成上色");

                } else {
                    try {
                        car.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                SleepUtil.sleep(1000);
            }
        }

    }
}
/**
 * 洗车
 */
public class CarWash implements Runnable {

    private Car car;

    public CarWash(Car c) {
        this.car = c;
    }

    
    @Override
    public void run() {

        synchronized (car) {

            while(true) {
                System.out.println(Thread.currentThread().getName() + "准备洗车");

                if ("INIT".equals(car.getState()) || "COLORED".equals(car.getState())) {

                    System.out.println("洗车中。。。。。。");
                    SleepUtil.sleep(3000);
                    car.setState("WASHED");
                    car.notify();
                    System.out.println(Thread.currentThread().getName() + "完成洗车");

                } else {
                    System.out.println(Thread.currentThread().getName() + "等待洗车");
                    try {
                        car.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                SleepUtil.sleep(1000);
            }
        }
    }
}
import com.example.thread.SleepUtil;

public class CarWorker {

    public static void main(String[] args) {

        Car car = new Car("INIT");

        CarWash carWash = new CarWash(car);
        CarColor carColor = new CarColor(car);

        Thread a = new Thread(carWash, "洗车位1");
        Thread b = new Thread(carWash, "洗车位2");
        Thread c = new Thread(carColor, "喷漆位");

        a.start();
        b.start();
        c.start();

        while(true) {
            SleepUtil.sleep(1000);
            System.out.println("---------------"+a.getName()+"->"+a.getState());
            System.out.println("---------------"+b.getName()+"->"+b.getState());
            System.out.println("---------------"+c.getName()+"->"+c.getState());
        }

    }

}

执行结果:

洗车位1准备洗车
洗车中。。。。。。
---------------洗车位1->TIMED_WAITING
---------------洗车位2->BLOCKED
---------------喷漆位->BLOCKED
---------------洗车位1->TIMED_WAITING
---------------洗车位2->BLOCKED
---------------喷漆位->BLOCKED
洗车位1完成洗车
---------------洗车位1->TIMED_WAITING
---------------洗车位2->BLOCKED
---------------喷漆位->BLOCKED
洗车位1准备洗车
洗车位1等待洗车
洗车位2准备洗车
洗车位2等待洗车
喷漆位准备上色
上色中。。。。。。
---------------洗车位1->WAITING
---------------洗车位2->WAITING
---------------喷漆位->TIMED_WAITING
---------------洗车位1->WAITING
---------------洗车位2->WAITING
---------------喷漆位->TIMED_WAITING
---------------洗车位1->WAITING
---------------洗车位2->WAITING
---------------喷漆位->TIMED_WAITING
喷漆位完成上色
---------------洗车位1->BLOCKED
---------------洗车位2->WAITING
---------------喷漆位->TIMED_WAITING
喷漆位准备上色
---------------洗车位1->TIMED_WAITING
---------------洗车位2->WAITING
---------------喷漆位->WAITING
洗车位1准备洗车
洗车中。。。。。。
---------------洗车位1->TIMED_WAITING
---------------洗车位2->WAITING
---------------喷漆位->WAITING
---------------洗车位1->TIMED_WAITING
---------------洗车位2->WAITING
---------------喷漆位->WAITING
---------------洗车位1->TIMED_WAITING
---------------洗车位2->WAITING
---------------喷漆位->WAITING
洗车位1完成洗车
---------------洗车位1->TIMED_WAITING
---------------洗车位2->BLOCKED
---------------喷漆位->WAITING
洗车位1准备洗车
洗车位1等待洗车
---------------洗车位1->WAITING
---------------洗车位2->TIMED_WAITING
---------------喷漆位->WAITING
洗车位2准备洗车
洗车位2等待洗车
---------------洗车位1->WAITING
---------------洗车位2->WAITING
---------------喷漆位->WAITING

Process finished with exit code -1

经典范式:

等待方遵循如下原则。
1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。
对应的伪代码如下。
synchronized(对象) {
while(条件不满足) {
对象.wait();
} 对
应的处理逻辑
}
通知方遵循如下原则。
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。
对应的伪代码如下。
synchronized(对象) {
改变条件
对象.notifyAll();
}

 三、Thread.join

  当前线程里如果存在其他线程A调用join()方法后,那么当前线程会等待线程A执行完之后,继续执行。

  场景:在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将可能早于子线程结束。如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束了。主线程可以sleep(xx),但这样的xx时间不好确定,因为子线程的执行时间不确定,join()方法比较合适这个场景。

  

import com.example.thread.SleepUtil;

public class JoinThread extends Thread{

    Thread thread;

    public Thread getThread() {
        return thread;
    }

    public void setThread(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "待执行");
        SleepUtil.sleep(1000);
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "执行");
    }
}
public class Main {

    public static void main(String[] args) {
        Thread threadPre = new JoinThread();
        for (int i = 0; i < 10; i++) {
            JoinThread joinThread = new JoinThread();
            joinThread.setThread(threadPre);

            joinThread.start();
            threadPre = joinThread;
        }
        System.out.println("ENDING......");
    }

}

执行结果:

Thread-1执行
Thread-2执行
Thread-3执行
Thread-4执行
Thread-5执行
Thread-6执行
Thread-7执行
Thread-8执行
Thread-9执行
Thread-10执行

 状态变化:

 

 状态验证代码:

public class JoinThread extends Thread{

    Thread thread;

    public Thread getThread() {
        return thread;
    }

    public void setThread(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "待执行");
        try {
            if (thread != null)
                thread.join();
            SleepUtil.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "执行");
    }
}
public static void main(String[] args) {

        JoinThread joinThread1 = new JoinThread();
        JoinThread joinThread2 = new JoinThread();
        JoinThread joinThread3 = new JoinThread();

        joinThread2.setThread(joinThread1);
        joinThread3.setThread(joinThread2);
        joinThread1.start();
        joinThread2.start();
        joinThread3.start();

        while (true) {
            System.out.println(joinThread1.getName()+"->"+joinThread1.getState());
            System.out.println(joinThread2.getName()+"->"+joinThread2.getState());
            System.out.println(joinThread3.getName()+"->"+joinThread3.getState());
            SleepUtil.sleep(500);
        }
    }

运行结果:

Thread-0->RUNNABLE
Thread-1->RUNNABLE
Thread-2->RUNNABLE
Thread-0待执行
Thread-1待执行
Thread-2待执行
Thread-0->TIMED_WAITING
Thread-1->WAITING
Thread-2->WAITING
Thread-0->TIMED_WAITING
Thread-1->WAITING
Thread-2->WAITING
Thread-0->TIMED_WAITING
Thread-1->WAITING
Thread-2->WAITING
Thread-0执行
Thread-0->TERMINATED
Thread-1->TIMED_WAITING
Thread-2->WAITING
Thread-0->TERMINATED
Thread-1->TIMED_WAITING
Thread-2->WAITING
Thread-0->TERMINATED
Thread-1->TIMED_WAITING
Thread-2->WAITING
Thread-0->TERMINATED
Thread-1->TIMED_WAITING
Thread-2->WAITING
Thread-1执行
Thread-0->TERMINATED
Thread-1->TERMINATED
Thread-2->TIMED_WAITING

 四、ThreadLocation

  线程私有局部变量的存储器,确保每个线程处理自己变量的副本,互不干扰。

代码演示:

public class ThreadLocationThread implements Runnable {

    ThreadLocal<Long> longThreadLocal = new ThreadLocal<Long>();

    Long aaa = 0L;

    @Override
    public void run() {
        if (longThreadLocal.get() == null) {
            longThreadLocal.set(0L);
        }
        longThreadLocal.set(longThreadLocal.get()+1);
        aaa++;
        System.out.println("longThreadLocal:"+longThreadLocal.get());
        System.out.println(aaa);

    }

    public static void main(String[] args) {
        ThreadLocationThread threadLocationThread = new ThreadLocationThread();
        new Thread(threadLocationThread).start();
        new Thread(threadLocationThread).start();
        new Thread(threadLocationThread).start();
        new Thread(threadLocationThread).start();
        new Thread(threadLocationThread).start();
        new Thread(threadLocationThread).start();

    }
}

运行结果:

longThreadLocal:1  --互不干扰
longThreadLocal:1
2
2
longThreadLocal:1
longThreadLocal:1
5
5
longThreadLocal:1
6
longThreadLocal:1
6

ThreadLocation原理

  查看源码ThreadLocal的set方法:

   public void set(T value) {
        Thread t = Thread.currentThread();
     //ThreadLocalMap为ThreadLocation内部类 ThreadLocalMap map
= getMap(t); //获取当前线程的Map对象 if (map != null)
       //set方法为Thread当前对象的ThreadLocal实例(每个线程实例都不一样,所以map中存储的值保证每个线程只能存储自己对象的value),value为要存储的值 map.set(
this, value); else createMap(t, value); }

 使用场景:

  • 处理较为复杂的业务时,使用ThreadLocal代替参数的显示传递。
  • ThreadLocal可以用来做数据库连接池保存Connection对象,这样就可以让线程内多次获取到的连接是同一个了(Spring中的DataSource就是使用的ThreadLocal)。
  • 管理Session会话,将Session保存在ThreadLocal中,使线程处理多次处理会话时始终是一个Session。

五、应用实例

  等待超时模式

  用一个方法等待一段时间(一般来定一个时间段),如果方法能定的时间段之内得到果,那么将果立刻返回,反之,超返回默认结

import com.example.thread.SleepUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class WaitTimeoutThread implements Runnable {

    List<String> list = new ArrayList<>();

    public static void main(String[] args) {
        WaitTimeoutThread waitTimeoutThread = new WaitTimeoutThread();

        new Thread(waitTimeoutThread).start();
        new Thread(waitTimeoutThread).start();
        new Thread(waitTimeoutThread).start();
        new Thread(waitTimeoutThread).start();
        waitTimeoutThread.testWait();
    }


    public String get() {

        if (list.size() <= 0) {
            return null;
        }

        String result = list.get(0);
        list.remove(0);
        return result;
    }

    @Override
    public void run() {

        while(true) {
            testRead(10000);
            SleepUtil.sleep(5000);
        }

    }

    public synchronized void testRead(long timeout1) {
        System.out.println(Thread.currentThread().getName()+"->"+new Date()+"取数据开始");
        long start = System.currentTimeMillis();

        //等待30秒
        Long waitLong = timeout1;
        long remaining = 1000L;
        boolean timeout = false;
        long end = start + waitLong;

        String result = get();

        while(result == null && !timeout) {
            try {
                //休息1秒钟
                wait(remaining);
                result = get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            timeout = System.currentTimeMillis() > end;
            if (timeout) {
                result = "超时了";
            }
        }
        System.out.println(Thread.currentThread().getName()+"->"+new Date()+result);
    }

    public String testWait() {
        while (true) {
            SleepUtil.sleep(3000);
            this.list.add(System.currentTimeMillis() + "");
            System.out.println("------------------------------>"+new Date()+"生成一个数据");
        }
    }
}

结果:

Thread-0->Thu May 13 14:22:05 CST 2021取数据开始
Thread-3->Thu May 13 14:22:05 CST 2021取数据开始
Thread-2->Thu May 13 14:22:05 CST 2021取数据开始
Thread-1->Thu May 13 14:22:05 CST 2021取数据开始
------------------------------>Thu May 13 14:22:08 CST 2021生成一个数据
Thread-3->Thu May 13 14:22:08 CST 20211620886928046
------------------------------>Thu May 13 14:22:11 CST 2021生成一个数据
Thread-0->Thu May 13 14:22:11 CST 20211620886931063
Thread-3->Thu May 13 14:22:13 CST 2021取数据开始
------------------------------>Thu May 13 14:22:14 CST 2021生成一个数据
Thread-3->Thu May 13 14:22:14 CST 20211620886934063
Thread-1->Thu May 13 14:22:15 CST 2021超时了
Thread-2->Thu May 13 14:22:15 CST 2021超时了
Thread-0->Thu May 13 14:22:16 CST 2021取数据开始
------------------------------>Thu May 13 14:22:17 CST 2021生成一个数据
Thread-0->Thu May 13 14:22:17 CST 20211620886937064
Thread-3->Thu May 13 14:22:19 CST 2021取数据开始
------------------------------>Thu May 13 14:22:20 CST 2021生成一个数据
Thread-3->Thu May 13 14:22:20 CST 20211620886940066
Thread-1->Thu May 13 14:22:20 CST 2021取数据开始
Thread-2->Thu May 13 14:22:20 CST 2021取数据开始

 

 六、线程池技术及示例

  频繁的创建线程,销毁线程是比较耗费系统资源。

  线程池技很好地解决问题,它建了若干数量的线程,并且不能由用直接对线程的行控制,在个前提下重复使用固定或较为固定数目的线程来完成任行。

  这样做的好是,一方面,消除了建和消亡线程的系统资源开,另一方面,面对过量任的提交能的劣化

  线程池例子代码:

import com.example.thread.SleepUtil;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

public class DefaultThreadPool<Job extends JobService> implements ThreadPool<Job> {

    //线程池最大线程数
    private static final int MAX_WORKER_NUMBERS = 10;

    //线程池默认线程数
    private static final  int DEFAULT_WORKER_NUMBERS = 5;

    //线程池最小线程数
    private static final int MIN_WORKER_NUMBERS  = 1;

    // 线程编号生成
    private AtomicLong threadNum = new AtomicLong();

    //工作线程队列
    private final LinkedList<Job> jobs = new LinkedList<>();
    //工作者线程队列
    private final List<Worker> workers = Collections.synchronizedList(new ArrayList<>());

    public DefaultThreadPool(int num){
        initWorkers(num);
    }

    @Override
    public void execute(Job job) {
        synchronized (jobs) {
            jobs.add(job);
            jobs.notify();
        }
    }

    @Override
    public void shutdown() {
        for(Worker worker: workers) {
            worker.shutdown();
        }
    }

    @Override
    public void addWorkers(int num) {
        synchronized (jobs) {
                initWorkers(num);
        }

    }

    @Override
    public void removeWorkers(int num) {
        synchronized (jobs) {
            for (Worker worker : workers) {
                worker.shutdown();
                workers.remove(worker);
            }
        }
    }

    @Override
    public int getJobSize() {
        return jobs.size();
    }

    /**
     * 初始化线程池,可用线程
     * @param num
     */
    public void initWorkers(int num) {
        for (int i = 0;i < num; i++) {
            Worker worker = new Worker();
            Thread thread = new Thread(worker, "Thread-Worker-"+threadNum.incrementAndGet());
            this.workers.add(worker);
            thread.start();
        }
    }

    /**
     * 负责消费任务
     */
    class Worker implements Runnable {
        //是否工作(线程同步)
        private volatile boolean running = true;

        @Override
        public void run() {
            while(running) {
                Job job = null;
                synchronized (jobs) {
                    if (jobs.isEmpty()) {
                        //任务列表为空
                        try {
                            jobs.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    job = jobs.removeFirst();
                }
                if (job != null) {
                    //???????? job.run();
                    System.out.println(Thread.currentThread().getName()+"开始执行");
                    job.exe();
                }
            }
        }
        public void shutdown() {
            running = false;
        }
    }

    public static void main(String[] args) {
        ThreadPool pool = new DefaultThreadPool(5);
        for(int i = 0;i < 110;i++) {
            SleepUtil.sleep(3000);
            String tmp = i+"";
            pool.execute(() -> {
                System.out.println("hello"+tmp);
            });
        }
    }
}
public interface JobService {
    public void exe();
}
public interface ThreadPool<Job extends JobService>  {

    //执行一个job
    void execute(Job job);

    //关闭线程池
    void shutdown();

    //增加线程
    void addWorkers(int num);

    //较少线程
    void removeWorkers(int num);

    //获得正在等待的执行的线程数量
    int getJobSize();

}

执行效果:

Connected to the target VM, address: '127.0.0.1:14108', transport: 'socket'
Thread-Worker-1开始执行
hello0
Thread-Worker-2开始执行
hello1
Thread-Worker-3开始执行
hello2
Thread-Worker-4开始执行
hello3
Thread-Worker-5开始执行
hello4
Thread-Worker-1开始执行
hello5
Thread-Worker-2开始执行
hello6
Thread-Worker-3开始执行
hello7
Thread-Worker-4开始执行
hello8
Thread-Worker-5开始执行
hello9

 

 

 

posted on 2021-05-12 17:58  耗子0114  阅读(49)  评论(0编辑  收藏  举报

导航