关于一些基础的Java问题的解答(五)
21. 实现多线程的两种方法:Thread与Runable
1.继承Thread类,重写run方法
- public class Test {
- public static void main(String[] args) {
- new MyThread().start();
- }
- private static class MyThread extends Thread {
- @Override
- public void run() {
- System.out.println("run!");
- }
- }
- }
2.实现Runnable接口,作为参数传入Thread构造函数
- public class Test {
- public static void main(String[] args) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("run!");
- }
- }).start();
- }
- }
3.使用ExecutorService类
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class Test {
- public static void main(String[] args) {
- ExecutorService service = Executors.newCachedThreadPool();
- service.execute(new Runnable() {
- @Override
- public void run() {
- System.out.println("Run!");
- }
- });
- }
- }
22. 线程同步的方法:sychronized、lock、reentrantLock等
- public class Test {
- private static int value = 0;
- public static void main(String[] args) {
- Test test = new Test();
- // 创建两个线程
- MyThread thread1 = test.new MyThread();
- MyThread thread2 = test.new MyThread();
- thread1.start();
- thread2.start();
- }
- /**
- * 为静态变量value加2
- * @return
- */
- public int next() {
- value++;
- Thread.yield(); // 加速问题的产生
- value++;
- return value;
- }
- /**
- * 判断是否偶数
- * @param num
- * @return boolean 是否偶数
- */
- public boolean isEven(int num) {
- return num % 2 == 0;
- }
- class MyThread extends Thread {
- @Override
- public void run() {
- System.out.println(Thread.currentThread() + " start!");
- while(isEven(next()));
- System.out.println(Thread.currentThread() + " down!");
- }
- }
- }
上面的代码创建了两个线程操作Test类中的静态变量value,调用next方法每次会为value的值加2,理论上来说isEven方法的返回值应该总是true,两个线程的工作会不停止的执行下去。但事实是:
1.synchronized
- /**
- * 为静态变量value加2
- * @return
- */
- public synchronized int next() {
- value++;
- Thread.yield(); // 加速问题的产生
- value++;
- return value;
- }
- /**
- * 为静态变量value加2
- *
- * @return
- */
- public int next() {
- synchronized (this) {
- value++;
- Thread.yield(); // 加速问题的产生
- value++;
- return value;
- }
- }
在synchronized关键字后的小括号内加入要加锁的对象即可。通过这种方法分离出来的代码段被称为临界区,也叫作同步控制块。
加入了synchronized后,在一个线程访问next方法的时候,另一个线程就无法访问next方法了,使得两个线程的工作互不干扰,循环也变得根本停不下来:
2.ReentrantLock
- private static Lock lock = new ReentrantLock();
- /**
- * 为静态变量value加2
- * @return
- */
- public int next() {
- lock.lock();
- try {
- value++;
- Thread.yield(); // 加速问题的产生
- value++;
- return value;
- } finally {
- lock.unlock();
- }
- }
一般而言,当我们使用synchronized时,需要写的代码量更少,因此通常只有我们在解决某些特殊问题时,才需要使用到Lock对象,比如尝试去获得锁:
- /**
- * 为静态变量value加2
- * @return
- */
- public int next() {
- boolean getLock = lock.tryLock();
- if (getLock) {
- try {
- value++;
- Thread.yield(); // 加速问题的产生
- value++;
- return value;
- } finally {
- lock.unlock();
- }
- } else {
- // do something else
- System.out.println(Thread.currentThread() + "say : I don't get the lock, QAQ");
- return 0;
- }
- }
除了ReentrantLock外,Lock类还有众多子类锁,在此不做深入讨论。值得注意的是,很明显,使用Lock通常会比使用synchronized高效许多,但我们并发编程时都应该从synchronized关键字入手,只有在性能调优时才替换为Lock对象这种做法。
23. 锁的等级:对象锁、类锁
- public synchronized void f() {};
- public void g() {
- synchronized (this) {
- }
- }
另外,synchronized也可以用来锁定类的静态方法和其中的代码块,此时关键字就是为类(类的Class对象)加锁了,因此被称为类锁:
- public class Test {
- public static synchronized void f() {};
- public static void g() {
- synchronized (Test.class) {
- }
- }
- }
24. 写出生产者消费者模式
- wait和notify方法
- await和signal方法
- BlockingQueue阻塞队列方法
- PipedInputStream和PipedOutputStream管道流方法
- import java.util.LinkedList;
- import java.util.Queue;
- class MyQueue {
- Queue<Integer> q;
- int size; // 队列持有产品数
- final int MAX_SIZE = 5; // 队列最大容量
- public MyQueue() {
- q = new LinkedList<>();
- size = 0;
- }
- /**
- * 生产产品
- *
- * @param num
- * 产品号码
- */
- public synchronized void produce(int num) {
- // 容量不足时,等待消费者消费
- try {
- while (size > MAX_SIZE)
- wait();
- } catch (InterruptedException e) {
- }
- ;
- System.out.println("produce " + num);
- q.add(num);
- size++;
- // 提醒消费者消费
- notifyAll();
- }
- /**
- * 消费产品
- */
- public synchronized void comsume() {
- // 没有产品时,等待生产
- try {
- while (size < 1)
- wait();
- } catch (InterruptedException e) {
- }
- ;
- System.out.println("comsume " + q.poll());
- size--;
- // 提醒生产者生产
- notifyAll();
- }
- }
- class Producer extends Thread {
- private MyQueue q;
- public Producer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.produce(i);
- }
- }
- class Consumer extends Thread {
- private MyQueue q;
- public Consumer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.comsume();
- }
- }
- public class Test {
- public static void main(String[] args) {
- MyQueue q = new MyQueue();
- Producer producer = new Producer(q);
- Consumer consumer = new Consumer(q);
- producer.start();
- consumer.start();
- }
- }
- import java.util.LinkedList;
- import java.util.Queue;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- class MyQueue {
- Queue<Integer> q;
- int size; // 队列持有产品数
- final int MAX_SIZE = 5; // 队列最大容量
- private Lock lock; // 锁
- private Condition condition; // 条件变量
- public MyQueue() {
- q = new LinkedList<>();
- size = 0;
- lock = new ReentrantLock();
- condition = lock.newCondition();
- }
- /**
- * 生产产品
- *
- * @param num
- * 产品号码
- */
- public void produce(int num) {
- // 进入临界区上锁
- lock.lock();
- // 容量不足时,等待消费者消费
- try {
- while (size > MAX_SIZE)
- condition.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- };
- System.out.println("produce " + num);
- q.add(num);
- size++;
- // 提醒消费者消费
- condition.signalAll();
- // 退出临界区解锁
- lock.unlock();
- }
- /**
- * 消费产品
- */
- public void comsume() {
- // 上锁进入临界区
- lock.lock();
- // 没有产品时,等待生产
- try {
- while (size < 1)
- condition.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- };
- System.out.println("comsume " + q.poll());
- size--;
- // 提醒生产者生产
- condition.signalAll();
- // 退出临界区解锁
- lock.unlock();
- }
- }
- class Producer extends Thread {
- private MyQueue q;
- public Producer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.produce(i);
- }
- }
- class Consumer extends Thread {
- private MyQueue q;
- public Consumer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.comsume();
- }
- }
- public class Main {
- public static void main(String[] args) {
- MyQueue q = new MyQueue();
- Producer producer = new Producer(q);
- Consumer consumer = new Consumer(q);
- producer.start();
- consumer.start();
- }
- }
第三种方法(BlockingQueue阻塞队列)实现:
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.LinkedBlockingQueue;
- class MyQueue {
- BlockingQueue<Integer> q; // 阻塞队列
- int size; // 队列持有产品数(此例无用)
- final int MAX_SIZE = 5; // 队列最大容量
- public MyQueue() {
- q = new LinkedBlockingQueue<>(MAX_SIZE);
- }
- /**
- * 生产产品
- *
- * @param num
- * 产品号码
- */
- public void produce(int num) {
- // 阻塞队列会自动阻塞,不需要处理
- try {
- q.put(num);
- System.out.println("produce " + num);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- /**
- * 消费产品
- */
- public void comsume() {
- // 阻塞队列会自动阻塞,不需要处理
- try {
- System.out.println("comsume " + q.take());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- class Producer extends Thread {
- private MyQueue q;
- public Producer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.produce(i);
- }
- }
- class Consumer extends Thread {
- private MyQueue q;
- public Consumer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.comsume();
- }
- }
- public class Main {
- public static void main(String[] args) {
- MyQueue q = new MyQueue();
- Producer producer = new Producer(q);
- Consumer consumer = new Consumer(q);
- producer.start();
- consumer.start();
- }
- }
第四种方法(PipedInputStream和PipedOutputStream):
- import java.io.PipedInputStream;
- import java.io.PipedOutputStream;
- class MyQueue {
- int size; // 队列持有产品数(此例无用)
- final int MAX_SIZE = 5; // 队列最大容量
- PipedInputStream pis;
- PipedOutputStream pos;
- public MyQueue() {
- // 初始化流
- pis = new PipedInputStream(MAX_SIZE);
- pos = new PipedOutputStream();
- // 管道流建立连接
- try {
- pos.connect(pis);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 生产产品
- *
- * @param num
- * 产品号码
- */
- public void produce(int num) {
- // 管道流会自动阻塞,不需要处理
- try {
- // 输出写在前面,否则会有奇怪的事情发生~
- System.out.println("produce " + num);
- pos.write(num);
- pos.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 消费产品
- */
- public void comsume() {
- // 管道流会自动阻塞,不需要处理
- try {
- System.out.println("comsume " + pis.read());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void finalize() throws Throwable {
- pis.close();
- pos.close();
- super.finalize();
- }
- }
- class Producer extends Thread {
- private MyQueue q;
- public Producer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.produce(i);
- }
- }
- class Consumer extends Thread {
- private MyQueue q;
- public Consumer(MyQueue q) {
- this.q = q;
- }
- @Override
- public void run() {
- for (int i = 0; i < 10; i++)
- q.comsume();
- }
- }
- public class Main {
- public static void main(String[] args) {
- MyQueue q = new MyQueue();
- Producer producer = new Producer(q);
- Consumer consumer = new Consumer(q);
- producer.start();
- consumer.start();
- }
- }
输出结果:
25. ThreadLocal的设计理念与作用
- import java.util.Random;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- class Accessor implements Runnable {
- private final int id; // 线程id
- public Accessor(int id) {
- this.id = id;
- }
- @Override
- public void run() {
- while(!Thread.currentThread().isInterrupted()) {
- ThreadLocalVariableHolder.increment();
- System.out.println(this);
- Thread.yield();
- }
- }
- @Override
- public String toString() {
- return "#" + id + " : " + ThreadLocalVariableHolder.get();
- }
- }
- public class ThreadLocalVariableHolder {
- private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
- // 返回随机数作为初始值
- protected Integer initialValue() {
- return new Random().nextInt(10000);
- }
- };
- /**
- * 为当前线程的value值加一
- */
- public static void increment() {
- value.set(value.get() + 1);
- }
- /**
- * 返回当前线程存储的value值
- * @return
- */
- public static int get() {
- return value.get();
- }
- public static void main(String[] args) throws InterruptedException {
- ExecutorService service = Executors.newCachedThreadPool();
- // 开启5个线程
- for (int i = 0; i < 5; i++)
- service.execute(new Accessor(i));
- // 所有线程运行3秒
- TimeUnit.SECONDS.sleep(1);
- // 关闭所有线程
- service.shutdownNow();
- }
- }
在上面的例子中虽然多个线程都去调用了ThreadLocalVariableHolder的increment和get方法,但这两个方法都没有进行同步处理,这是因为ThreadLocal保证我们使用的时候不会出现竞争条件。从结果来看,每个线程都在单独操作自己的变量,每个单独的线程都被分配了自己的存储(即便只有一个ThreadLocalVariableHolder对象),线程之间并没有互相造成影响。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
关于一些基础的Java问题的解答(五)的更多相关文章
- 关于一些基础的Java问题的解答(一)
学习一门语言基础是非常重要的,因此本文总结了一些常见的Java基础问题的解答,希望可以帮到大家. 1. 九种基本数据类型的大小,以及他们的封装类. 9种基本数据类型 基本类型 包装类型 大小 bool ...
- 关于一些基础的Java问题的解答(七)
31. 反射的作用与原理 简单的来说,反射机制其实就是指程序在运行的时候能够获取自身的信息.如果知道一个类的名称或者它的一个实例对象, 就能把这个类的所有方法和变量的信息(方法名,变量名,方法,修饰符 ...
- 关于一些基础的Java问题的解答(六)
26. ThreadPool用法与优势 ThreadPool即线程池,它是JDK1.5引入的Concurrent包中用于处理并发编程的工具.使用线程池有如下好处: 降低资源消耗:通过重复利用已创建的线 ...
- 关于一些基础的Java问题的解答(四)
16. Java面向对象的三个特征与含义 java中的面向对象的三大基本特征分别是:封装.继承.多态: 封装:把过程和数据包围起来,对数据的访问只能通过已定义的界面,主要是方便类的修改 继承:对象的一 ...
- 关于一些基础的Java问题的解答(三)
11. HashMap和ConcurrentHashMap的区别 从JDK1.2起,就有了HashMap,正如上一个问题所提到的,HashMap与HashTable不同,不是线程安全的,因此多线程 ...
- 关于一些基础的Java问题的解答(二)
6. Hashcode的作用 官方对于hashCode的解释如下: Whenever it is invoked on the same object more than once during an ...
- 黑马程序员:Java基础总结----java注解
黑马程序员:Java基础总结 java注解 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! java注解 lang包中的基本注解 @SuppressWarnings ...
- java面试题—精选30道Java笔试题解答(二)
摘要: java面试题-精选30道Java笔试题解答(二) 19. 下面程序能正常运行吗() public class NULL { public static void haha(){ System ...
- JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题
JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...
随机推荐
- Python-面向对象(一)-Day7
Day7-面向对象基础 1一.isinstance(obj, cls) 1二.issubclass(sub, super) 1三.异常处理 11.异常基础 12.异常种类 23.异常其他结构 54.主 ...
- 离线Chrome插件安装文件(crx)的安装方法
离线Chrome插件安装文件(crx)的安装方法 一.正常安装方法 1.开发谷歌浏览器,设置->扩展程序 在打开的谷歌浏览器的扩展管理器中用户可以看到一些已经安装程序的Chrome插件,或者一个 ...
- Extensions in UWP Community Toolkit - Mouse Cursor
概述 UWP Community Toolkit Extensions 中有一个为 Mouse 提供的扩展 - Mouse Cursor Extensions,本篇我们结合代码详细讲解 Mouse C ...
- Lua编写wireshark插件初探——解析Websocket上的MQTT协议
一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...
- Android自定义圆形图片工具类(CTRL+C加CTRL+V直接使用)
先贴一下工具类的代码!可直接复制粘贴 public class RoundImageView extends ImageView { private Paint mPaint; //画笔 privat ...
- JavaScript 以及 css3进度条
JavaScript css3进度条 使用css3实现进度条 <!DOCTYPE html> <html lang="en"> <head> & ...
- geotrellis使用(三十七)COG 基础介绍
前言 Geotrellis 已经迭代到了 2.0 版本(截止写作此文时为 2.0.0-SNAPSHOT 版),2.0 版多了很多新的特性,其中最重要的应该就是 COG,COG 是什么鬼?刚看到时我也是 ...
- hdu1022 Train Problem I---模拟栈
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1022 题目大意: 车的进出站问题,先给出N个火车,再按序列一的方式进站,判断能否以序列二的方式出站,若 ...
- global与nonlocal关键字总结
global适用于函数内部修改全局变量的值 nonlocal适用于嵌套函数中内部函数修改外部变量的值 如果没有使用以上关键字,对全局变量或者外部变量进行修改,python会默认将全局变量隐藏起来 例1 ...
- web攻击和防御措施
1.SQL注入:参照下面的链接 http://www.cnblogs.com/chenhaoyu/p/8758888.html 2.跨网站脚本攻击(Cross Site Scripting, XSS) ...