这篇文章主要是用简单的代码展示自己在梳理和复习多线程的过程中的一些问题,欢迎大家指正;
- 关键字volatile
/** * @program: demo * @description: ${description} * @author: Will Wu * @create: 2018-12-06 20:22 **/ @Slf4j public class Counter { //volatile只能修饰变量,原意是"不稳定的,易挥发的",在java中修饰变量,保证了该变量的立即可见性,一旦一个线程修改了该变量的值,其他线程能立马获取到该变量最新的值 //另外,需要注意,volatile修饰变量,其本质就是告诉jvm,表示该变量的值不稳定,不应该从缓存区中获取该变量的值,应该从主存区中获取该变量的值 private volatile int count=0; public void inc(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; } @Override public String toString() { return "[count="+count+"]"; } public static void main(String[] args) { Counter counter = new Counter(); for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { counter.inc(); } }).start(); } System.out.println(counter); } }
- 死锁
/** * @program: 死锁类 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 10:58 **/ public class DeadLock implements Runnable{ public int flag=1; private static Object o1=new Object(),o2=new Object(); @Override public void run() { System.out.println("flag="+flag); if (flag==1){ synchronized (o1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ System.out.println("1"); } } } if (flag==0) { synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("0"); } } } } public static void main(String[] args) { DeadLock lock1 = new DeadLock(); DeadLock lock2 = new DeadLock(); lock1.flag=1; lock2.flag=0; new Thread(lock1).start(); new Thread(lock2).start(); } }
- 如何避免死锁?
/** * @program: * 避免死锁方法一:加锁顺序,即线程按照一定的顺序加锁 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 11:19 **/ public class ResDeadLockOne { public int flag=1; //static 修饰的变量为成员变量,为类所有,即类的所有实例对象共有 private static Object o1=new Object(),o2=new Object(); public void money(int flag){ this.flag=flag; if (flag==1){ synchronized (o1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ System.out.println("当前线程:"+Thread.currentThread().getName()+",flag="+flag); } } } if (flag==0){ synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("当前线程:"+Thread.currentThread().getName()+",flag="+flag); } } } } public static void main(String[] args) { final ResDeadLockOne td1 = new ResDeadLockOne(); final ResDeadLockOne td2 = new ResDeadLockOne(); td1.flag=1; td2.flag=0; Thread t1= new Thread(new Runnable() { @Override public void run() { td1.flag = 1; td1.money(1); } }); t1.start(); Thread t2= new Thread(new Runnable() { @Override public void run() { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } td2.flag=0; td2.money(0); } }); t2.start(); } }
** * @program: 线程死锁解决方法二: * 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁) * @description: ${description} * @author: Will Wu * @create: 2018-12-09 11:37 **/ @Slf4j public class ResDeadLockTwo { public int flag=1; private static Object o1=new Object(),o2=new Object(); public void money(int flag){ this.flag=flag; if (flag==1){ synchronized (o1){ try { Thread.sleep(1000); synchronized (o2){ System.out.println("当前线程:"+Thread.currentThread().getName()+",flag="+flag); } } catch (InterruptedException e) { e.printStackTrace(); } } } if (flag==0){ synchronized (o2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("当前线程:"+Thread.currentThread().getName()+",flag="+flag); } } } } public static void main(String[] args) { ResDeadLockTwo td1 = new ResDeadLockTwo(); ResDeadLockTwo td2 = new ResDeadLockTwo(); final ReentrantLock lock = new ReentrantLock(); td1.flag=1; td2.flag=0; Thread t1 = new Thread(new Runnable() { @Override public void run() { String tname=Thread.currentThread().getName(); td1.flag=1; try { //获取不到锁,等待五秒,如果超时,就返回false if(lock.tryLock(5000, TimeUnit.MILLISECONDS)){ System.out.println(tname+"获得锁..."); }else { System.out.println(tname+"没有获得锁..."); return; } } catch (InterruptedException e) { e.printStackTrace(); } try { td1.money(1); } catch (Exception e) { e.printStackTrace(); System.out.println(tname+"出错啦..."); } finally { System.out.println("当前线程:"+tname); lock.unlock(); } } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { String tname = Thread.currentThread().getName(); td2.flag = 0; try { //获取不到锁,等待五秒,如果超时,就返回false if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) { System.out.println(tname + "获得锁..."); } else { System.out.println(tname + "没有获得锁..."); return; } } catch (InterruptedException e) { e.printStackTrace(); } try { td2.money(1); } catch (Exception e) { e.printStackTrace(); System.out.println(tname+"出错啦..."); } finally { System.out.println("当前线程:"+tname); lock.unlock(); } } }); t2.start(); } }
- 多个线程同时访问同一个类中的多个加锁方法
/** * @program: 多个线程不可访问同一个类中的 2 个加了 Lock 锁的方法 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 09:53 **/ @Slf4j public class MultiThread { private int count=0; //lock private Lock lock=new ReentrantLock(); private Runnable runnableone=new Runnable() { @Override public void run() { //lock lock.lock(); while (count<100){ try { //是否执行该方法 log.info(Thread.currentThread().getName()+"run 1:"+count++); } catch (Exception e) { e.printStackTrace(); } } lock.unlock(); } }; private Runnable runnabletwo=new Runnable() { @Override public void run() { lock.lock(); while(count<100){ log.info(Thread.currentThread().getName()+"run 2:"+count++); } lock.unlock(); } }; public static void main(String[] args) { MultiThread qq = new MultiThread(); new Thread(qq.runnableone).start(); new Thread(qq.runnabletwo).start(); } }
/** * @program: * 使用 synchronized 时,当我们访问同一个类对象的时候,是同一把锁,所以可以访问 该对象的其他 synchronized 方法 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 10:09 **/ @Slf4j public class MultiThreadO { private int count=0; private Lock lock=new ReentrantLock(); public Runnable runnableone=new Runnable() { @Override public void run() { //设置关键字synchronized,以当前类为锁 synchronized (this){ while (count<100){ log.info(Thread.currentThread().getName()+"run 1:"+count++); } } } }; public Runnable runnabletwo=new Runnable() { @Override public void run() { //设置关键字synchronized,以当前类为锁 synchronized (this){ while (count<100){ log.info(Thread.currentThread().getName()+"run 2:"+count++); } } } }; public static void main(String[] args) { MultiThreadO multiThread = new MultiThreadO(); new Thread(multiThread.runnableone).start(); new Thread(multiThread.runnabletwo).start(); } }
- Callabel接口
/** * @program: demo * @description: ${description} * @author: Will Wu * @create: 2018-12-06 20:00 **/ @AllArgsConstructor @NoArgsConstructor @Slf4j public class MyCallable implements Callable <Object>{ private String taskNum; @Override public Object call() throws Exception { log.info("=======>"+taskNum+" 任务启动..."); Date date1 = new Date(); Thread.sleep(1000); Date date2= new Date(); long time=date2.getTime()-date1.getTime(); log.info("=======>"+taskNum+" 任务结束..."); return taskNum+"任务返回运行结果,当前任务时间耗时:" + time+"毫秒"; } public static void main(String[] args) throws ExecutionException, InterruptedException { log.info("程序开始运行..."); Date date = new Date(); int size=5; ExecutorService pool = Executors.newFixedThreadPool(size); ArrayList<Future> list = new ArrayList<>(); for(int i=0;i<size;i++){ MyCallable callable = new MyCallable(i + ""); Future<Object> future = pool.submit(callable); log.info(future.get().toString()); list.add(future); } pool.shutdown(); list.stream().collect(Collectors.toList()).forEach(System.out::println); } }
- 多线程之间通信
/** * @program: 多线程之间如何实现通信 * 方法一:共享变量 * 线程间通信可以通过发送信号,发送信号的一个简单方式是在共享对象的变量里面设置信号值 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 12:04 **/ @Slf4j public class MySignal { //共享的变量 @Setter@Getter private boolean hasDataToProcess=false; public static void main(String[] args) { //同一个对象 final MySignal mySignal = new MySignal(); Thread t1 = new Thread(() -> mySignal.setHasDataToProcess(true)); t1.start(); Thread t2 = new Thread(() -> { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } mySignal.isHasDataToProcess(); System.out.println("t1改变后的值:"+mySignal.isHasDataToProcess()); }); t2.start(); } }
/** * @program: 多线程之间如何实现通信 * 方法二:wait/notify--等待唤醒机制 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 12:17 **/ @Slf4j public class Resource { private String name; private int count=1; private boolean flag=false; public synchronized void set(String name){ while(flag){ try { //线程等待,消费之消费资源 wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag=true; //唤醒等待中的消费者 this.notifyAll(); } } public synchronized void out(String name){ while(!flag){ try { //线程等待,生产者生产资源 wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag=false; //唤醒等待中的消费者 this.notifyAll(); } } }
- 如何控制并发线程的个数
/** * @program: demo * 如何控制某个方法允许并发访问线程的个数 * @description: ${description} * @author: Will Wu * @create: 2018-12-08 10:06 **/ public class SeamphoreTest { //采用该Semaphore类,如下代码去控制test()方法最多五个线程并发访问 static Semaphore semaphore=new Semaphore(5,true); public static void main(String[] args) { for(int i=0;i<100;i++){ new Thread(() -> test()).start(); } } public static void test(){ try { //申请一个请求 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"is coming..."); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"is going..."); } catch (InterruptedException e) { e.printStackTrace(); } semaphore.release(); } }
- 几种常见的线程池
/** * @program: 线程池 * * @description: ${description} * @author: Will Wu * @create: 2018-12-04 21:24 **/ public class ThreadPool { static class MyThread extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"正在执行..."); } } public static void main(String[] args) throws ExecutionException, InterruptedException { //固定数量的线程池 // ExecutorService pool = Executors.newFixedThreadPool(2); //单任务线程池 // ExecutorService pool = Executors.newSingleThreadExecutor(); //可变线程池 // ExecutorService pool = Executors.newCachedThreadPool(); //定时线程池 // ScheduledExecutorService pool= Executors.newScheduledThreadPool(2); ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); HashSet<Callable<String>> set = new HashSet<>(); set.add(new Callable<String>() { @Override public String call() throws Exception { return "task1"; } }); set.add(new Callable<String>() { @Override public String call() throws Exception { return "task2"; } }); set.add(new Callable<String>() { @Override public String call() throws Exception { return "task3"; } }); // String s = singleThreadExecutor.invokeAny(set); // System.out.println("==========>"+s); List<Future<String>> futures = singleThreadExecutor.invokeAll(set); for (Future<String> future:futures){ System.out.println("==============>"+future.get()); } // Future<Object> objectFuture = pool.submit( // new Callable<Object>() { // @Override // public Object call() throws Exception { // return null; // } // } // ); // Future<?> future = pool.submit(new Runnable() { // @Override // public void run() { // System.out.println("Asynchronous task"); // } // }); // // Object o = future.get(); // System.out.println(o); // pool.execute(new Runnable() { // @Override // public void run() { // // } // }); // MyThread thread1 = new MyThread(); // MyThread thread2 = new MyThread(); // MyThread thread3 = new MyThread(); // MyThread thread4 = new MyThread(); // MyThread thread5 = new MyThread(); // // pool.execute(thread1); // pool.execute(thread2); // pool.execute(thread3); //// pool.execute(thread4); //// pool.execute(thread5); // pool.schedule(thread4,1000, TimeUnit.MILLISECONDS); // pool.schedule(thread5,2000, TimeUnit.MILLISECONDS); // // pool.shutdown(); } }
- 多线程的两个简单应用实例
/** * @program: * 三个线程 a、b、c 并发运行,b,c 需要 a 线程的数据怎么实现 * @description: ${description} * @author: Will Wu * @create: 2018-12-09 09:31 **/ @Slf4j public class ThreadCommunication { private static int num;//数据 //定义一个信号量,该类内部维持了多个线程锁,可以阻塞多个线程,释放多个线程,线程的阻塞和释放是通过Permit概念来实现的, //线程通过semaphore.acquire()获取permit,如果当前semaphore有permit则分配给该线程,没有则阻塞线程直到semaphore调用 //release()方法释放Permit,参数,代表permit允许的个数 private static Semaphore semaphore=new Semaphore(0); public static void main(String[] args) { //first thread a to generate num; Thread threadA = new Thread(() -> { try { Thread.sleep(1000); num=1; //初始化参数后是释放两个permit semaphore.release(2); } catch (InterruptedException e) { e.printStackTrace(); } }); //then thread b and c to use num Thread threadB = new Thread(() -> { try { //获取permit,如果没有可用的,则处于阻塞状态 semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } log.info(Thread.currentThread().getName() + "获取到num的值为:" + num); }); Thread threadC = new Thread(() -> { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } log.info(Thread.currentThread().getName() + "获取到num的值为:" + num); }); ; threadA.start(); threadB.start(); threadC.start(); } }
/** * @program: 子线程运行执行 10 次后,主线程再运行 5 次。这样交替执行三遍 * @description: ${description} * @author: Will Wu * @create: 2018-12-03 20:20 **/ public class ThreadTest { static class Business{ private boolean subFlag=false; public synchronized void mainMethod(){ while(subFlag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+": main thread runing count--"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } subFlag=true; notify(); } public synchronized void sumMethod(){ while(!subFlag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+": sub thread runing count--"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } subFlag=false; notify(); } } public static void main(String[] args) { final Business business=new Business(); Thread thread = new Thread(() -> { for (int i = 0; i < 3; i++) { business.sumMethod(); } }); thread.start(); for (int i=0;i<3;i++){ business.mainMethod(); } } }