一个简单的synchronized多线程问题、梳理与思考
一个程序,多个线程同时操作一个变量,给这个变量+1()。功能很简单,可是怎么样去实现呢?这其中涉及到了哪些问题?
最基础想法
见代码:
- public class Test extends Thread {
- public static int amount = 0;
- public void run() {
- amount++;
- }
- public static void main(String[] args) {
- int num_thread = 100;
- for (int i = 0; i < num_thread; i++) {
- new Test().start();
- }
- try {
- Thread.sleep(3 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(amount);
- }
- }
输出结果:
num_thread = 100时,结果=100;
num_thread = 1000时,结果=1000;
num_thread = 10000时,结果=9995;
num_thread = 1000000时,结果=999936;
程序判定为不安全,当线程数比较少的时候,因为线程是先后启动的,所以看起来没有影响,一旦线程数增大,弊端毕露无疑。其实还有一个更简单看出问题的方法,线程运行时,不是给变量+1,而是+1000*1000,再来看结果:
num_thread = 10时,结果=5034021;——线程数很少,但是结果不是想要的结果。
总之说明,这样的多线程不安全!amount++这个方法并不是原子性的!
升级想法1.0:用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新值。
见代码:
- public class Test extends Thread {
- public static volatile int amount = 0;//只是这里的变量声明为volatile修饰
- public void run() {
- int i = 0;
- while (i < 1000 * 1000) {
- amount++;
- i++;
- }
- }
- public static void main(String[] args) {
- int num_thread = 10;
- for (int i = 0; i < num_thread; i++) {
- new Test().start();
- }
- try {
- Thread.sleep(1 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(amount);
- }
- }
输出结果:
num_thread = 10时,结果=2375833;——结果仍然不是想要的。
那问题出在哪里了呢?处在了对volatile修饰符的理解上。(参考博客:java中volatile关键字的含义)
volatile很容易被误用,被误用来进行原子性操作。
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图描述这种交互:
read and load ——从主存复制变量到当前工作内存
use and assign ——执行代码,改变共享变量值
store and write ——用工作内存数据刷新主存相关内容
其中use and assign 可以多次出现,但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样。
对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的。
这项问题搞清楚之后可以继续想法了。
升级想法3.0:同步代码块——通过 synchronized 关键字,所有加上synchronized 的块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法或者代码块。
java为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。
对于synchronized的使用,又有不同的方式:同步代码块和同步方法
首先来看同步代码块的运用。语法见代码:
- synchronized(syncObject){
- //code
- }
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject 的锁方能执行。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
这里面的syncObject,可以是 类实例 或 类。
针对我们的场景,修改代码如下:
- public class Test extends Thread {
- public static volatile Integer amount = new Integer(0);//修改为对象
- public void run() {
- int i = 0;
- synchronized (amount) {
- while (i < 1000 * 1000) {
- amount++;
- i++;
- }
- }
- }
- public static void main(String[] args) {
- int num_thread = 10;
- for (int i = 0; i < num_thread; i++) {
- new Test().start();
- }
- try {
- Thread.sleep(1 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(amount);
- }
- }
变动包括,amount的类型由int变为Interger,这样amount才是一个可以被synchronized使用的Integer实例。
然而程序的输出:1902241 —— 仍然不是我们想要的。问题出在了哪里?测试发现在synchronized后面sleep 10ms 以上同步成功,sleep 1ms 的话就不会成功!!!!!!!!!!!什么鬼!!!!!!!!详情见代码注释:
- public class Test implements Runnable {
- // public static volatile AtomicInteger amount = new AtomicInteger(0);
- private Integer amount = new Integer(0);
- private InClass inClass = new InClass("adfas");
- public void run() {
- synchronized (amount) {
- // synchronized (inClass) {// 注意!!这里换成inClass就成功了,即便没有sleep,简直郁闷啊啊啊啊啊!!!
- // synchronized (this){ 或者synchronized (Test.class)都可以,即便没有sleep
- System.out.println(
- Thread.currentThread().getName() + "---------begin--------------" + System.currentTimeMillis());
- try {
- Thread.sleep(1);//sleep(10)的话可以成功,查看输出,程序输出时间确实是递增的!!!
- } catch (Exception e) {
- // TODO 自动生成的 catch 块
- e.printStackTrace();
- }
- for (int i = 0; i < 200000;) {
- // addOne();
- amount++;
- i++;
- }
- System.out.println(
- Thread.currentThread().getName() + "------------end-------------" + System.currentTimeMillis());
- }
- }
- public synchronized void addOne() {
- amount++;
- }
- public static void main(String[] args) {
- int num_thread = 50;
- Test test = new Test();
- for (int i = 0; i < num_thread; i++) {
- (new Thread(test)).start();
- }
- try {
- Thread.sleep(10 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(test.amount);
- }
- class InClass {
- public InClass(String name) {
- // TODO 自动生成的构造函数存根
- }
- }
- }
对于上面这个例子,是我最大的疑惑。讲道理的话,amount是一个Integer的实例,我本认为synchronized(amount)锁住的是amount这个实例的同步方法或者同步代码块,按道理来说对程序是没有影响的,也就是synchronized并不起作用,但是使用inClass实例告诉我们,上面的想法是错误的(具体原因需要分析)!但是既然synchronized(syncObject)表明了可以用的话,amount抽象来看,和inClass是同一个东西,都是实例!但是amount为什么就不能用呢???感觉非常奇怪,贴一下测试结果:
测试结果1:synchronized(amount)没有sleep(),可以明显看出,代码块没有被锁住。另外,sleep(1)的时候,结果类似。
- Thread-0---------begin--------------1483240909596
- Thread-7---------begin--------------1483240909596
- Thread-9---------begin--------------1483240909597
- Thread-10---------begin--------------1483240909597
- Thread-11---------begin--------------1483240909598
- Thread-12---------begin--------------1483240909603
- Thread-8---------begin--------------1483240909617
- Thread-18---------begin--------------1483240909617
- Thread-9------------end-------------1483240909628
- Thread-17---------begin--------------1483240909628
- Thread-10------------end-------------1483240909631
- Thread-12------------end-------------1483240909631
- Thread-16---------begin--------------1483240909632
- Thread-20---------begin--------------1483240909632
- Thread-11------------end-------------1483240909632
- Thread-19---------begin--------------1483240909633
- Thread-18------------end-------------1483240909636
- Thread-13---------begin--------------1483240909636
- Thread-14---------begin--------------1483240909632
- Thread-15---------begin--------------1483240909651
- Thread-48---------begin--------------1483240909651
- Thread-49---------begin--------------1483240909654
- Thread-20------------end-------------1483240909654
- Thread-13------------end-------------1483240909655
- Thread-22---------begin--------------1483240909651
- Thread-30---------begin--------------1483240909651
- Thread-14------------end-------------1483240909669
- Thread-23---------begin--------------1483240909651
- Thread-15------------end-------------1483240909670
- Thread-16------------end-------------1483240909651
- Thread-7------------end-------------1483240909642
- Thread-31---------begin--------------1483240909651
- Thread-48------------end-------------1483240909673
- Thread-38---------begin--------------1483240909651
- Thread-39---------begin--------------1483240909651
- Thread-30------------end-------------1483240909674
- Thread-46---------begin--------------1483240909651
- Thread-22------------end-------------1483240909674
- Thread-26---------begin--------------1483240909651
- Thread-23------------end-------------1483240909686
- Thread-34---------begin--------------1483240909650
- Thread-42---------begin--------------1483240909650
- Thread-21---------begin--------------1483240909650
- Thread-25---------begin--------------1483240909650
- Thread-38------------end-------------1483240909693
- Thread-31------------end-------------1483240909694
- Thread-28---------begin--------------1483240909650
- Thread-26------------end-------------1483240909695
- Thread-29---------begin--------------1483240909650
- Thread-37---------begin--------------1483240909650
- Thread-36---------begin--------------1483240909650
- Thread-34------------end-------------1483240909705
- Thread-25------------end-------------1483240909709
- Thread-44---------begin--------------1483240909650
- Thread-21------------end-------------1483240909711
- Thread-33---------begin--------------1483240909650
- Thread-28------------end-------------1483240909713
- Thread-41---------begin--------------1483240909650
- Thread-45---------begin--------------1483240909650
- Thread-29------------end-------------1483240909713
- Thread-35---------begin--------------1483240909650
- Thread-42------------end-------------1483240909714
- Thread-27---------begin--------------1483240909650
- Thread-17------------end-------------1483240909650
- Thread-24---------begin--------------1483240909650
- Thread-32---------begin--------------1483240909650
- Thread-44------------end-------------1483240909730
- Thread-40---------begin--------------1483240909650
- Thread-41------------end-------------1483240909732
- Thread-35------------end-------------1483240909733
- Thread-19------------end-------------1483240909650
- Thread-43---------begin--------------1483240909650
- Thread-0------------end-------------1483240909650
- Thread-6---------begin--------------1483240909734
- Thread-8------------end-------------1483240909644
- Thread-45------------end-------------1483240909735
- Thread-33------------end-------------1483240909733
- Thread-37------------end-------------1483240909722
- Thread-36------------end-------------1483240909722
- Thread-39------------end-------------1483240909691
- Thread-46------------end-------------1483240909691
- Thread-49------------end-------------1483240909674
- Thread-47---------begin--------------1483240909657
- Thread-27------------end-------------1483240909740
- Thread-24------------end-------------1483240909740
- Thread-32------------end-------------1483240909741
- Thread-40------------end-------------1483240909748
- Thread-6------------end-------------1483240909751
- Thread-5---------begin--------------1483240909751
- Thread-43------------end-------------1483240909751
- Thread-47------------end-------------1483240909751
- Thread-5------------end-------------1483240909752
- Thread-4---------begin--------------1483240909752
- Thread-4------------end-------------1483240909754
- Thread-3---------begin--------------1483240909754
- Thread-3------------end-------------1483240909756
- Thread-2---------begin--------------1483240909756
- Thread-2------------end-------------1483240909757
- Thread-1---------begin--------------1483240909757
- Thread-1------------end-------------1483240909758
- 2589439
测试结果2:synchronized(amount)并且sleep(10),可以看出,代码是被同步了的。为了看的更清楚,sleep(1000),结果类似。
- Thread-0---------begin--------------1483241058337
- Thread-0------------end-------------1483241058353
- Thread-49---------begin--------------1483241058353
- Thread-49------------end-------------1483241058365
- Thread-48---------begin--------------1483241058365
- Thread-48------------end-------------1483241058378
- Thread-47---------begin--------------1483241058379
- Thread-47------------end-------------1483241058390
- Thread-46---------begin--------------1483241058390
- Thread-46------------end-------------1483241058401
- Thread-45---------begin--------------1483241058401
- Thread-45------------end-------------1483241058412
- Thread-44---------begin--------------1483241058412
- Thread-44------------end-------------1483241058423
- Thread-43---------begin--------------1483241058423
- Thread-43------------end-------------1483241058434
- Thread-42---------begin--------------1483241058434
- Thread-42------------end-------------1483241058446
- Thread-41---------begin--------------1483241058446
- Thread-41------------end-------------1483241058461
- Thread-40---------begin--------------1483241058461
- Thread-40------------end-------------1483241058472
- Thread-39---------begin--------------1483241058472
- Thread-39------------end-------------1483241058483
- Thread-38---------begin--------------1483241058483
- Thread-38------------end-------------1483241058494
- Thread-37---------begin--------------1483241058494
- Thread-37------------end-------------1483241058504
- Thread-36---------begin--------------1483241058504
- Thread-36------------end-------------1483241058515
- Thread-35---------begin--------------1483241058516
- Thread-35------------end-------------1483241058526
- Thread-34---------begin--------------1483241058526
- Thread-34------------end-------------1483241058540
- Thread-33---------begin--------------1483241058541
- Thread-33------------end-------------1483241058552
- Thread-32---------begin--------------1483241058552
- Thread-32------------end-------------1483241058563
- Thread-31---------begin--------------1483241058564
- Thread-31------------end-------------1483241058577
- Thread-29---------begin--------------1483241058577
- Thread-29------------end-------------1483241058588
- Thread-30---------begin--------------1483241058588
- Thread-30------------end-------------1483241058598
- Thread-28---------begin--------------1483241058599
- Thread-28------------end-------------1483241058610
- Thread-27---------begin--------------1483241058610
- Thread-27------------end-------------1483241058621
- Thread-26---------begin--------------1483241058621
- Thread-26------------end-------------1483241058632
- Thread-25---------begin--------------1483241058632
- Thread-25------------end-------------1483241058643
- Thread-24---------begin--------------1483241058643
- Thread-24------------end-------------1483241058654
- Thread-23---------begin--------------1483241058654
- Thread-23------------end-------------1483241058665
- Thread-22---------begin--------------1483241058665
- Thread-22------------end-------------1483241058680
- Thread-21---------begin--------------1483241058680
- Thread-21------------end-------------1483241058693
- Thread-20---------begin--------------1483241058693
- Thread-20------------end-------------1483241058706
- Thread-19---------begin--------------1483241058706
- Thread-19------------end-------------1483241058718
- Thread-18---------begin--------------1483241058718
- Thread-18------------end-------------1483241058731
- Thread-17---------begin--------------1483241058731
- Thread-17------------end-------------1483241058743
- Thread-16---------begin--------------1483241058743
- Thread-16------------end-------------1483241058757
- Thread-15---------begin--------------1483241058757
- Thread-15------------end-------------1483241058770
- Thread-14---------begin--------------1483241058772
- Thread-14------------end-------------1483241058783
- Thread-13---------begin--------------1483241058783
- Thread-13------------end-------------1483241058794
- Thread-12---------begin--------------1483241058794
- Thread-12------------end-------------1483241058805
- Thread-11---------begin--------------1483241058805
- Thread-11------------end-------------1483241058816
- Thread-10---------begin--------------1483241058817
- Thread-10------------end-------------1483241058828
- Thread-9---------begin--------------1483241058828
- Thread-9------------end-------------1483241058839
- Thread-8---------begin--------------1483241058839
- Thread-8------------end-------------1483241058850
- Thread-7---------begin--------------1483241058850
- Thread-7------------end-------------1483241058861
- Thread-6---------begin--------------1483241058861
- Thread-6------------end-------------1483241058872
- Thread-3---------begin--------------1483241058872
- Thread-3------------end-------------1483241058883
- Thread-5---------begin--------------1483241058883
- Thread-5------------end-------------1483241058895
- Thread-4---------begin--------------1483241058896
- Thread-4------------end-------------1483241058907
- Thread-2---------begin--------------1483241058907
- Thread-2------------end-------------1483241058919
- Thread-1---------begin--------------1483241058919
- Thread-1------------end-------------1483241058930
升级想法3.1:基于上面出现的各种问题,可以把amount++这一步直接用一个synchronized修饰的方法代替,简单明了,粗暴高效!
详见上面的代码里面的addOne()方法!但是这里其实是有问题的:毕竟真是程序中一般可能出现各种同步情况,很多时候同步代码块儿的灵活性非常好,而同步方法使用起来可能不方便;所以上面的问题还是需要解决,在网上很多的例子中我们看到的都是synchronized(this)和synchronized(Test.class),关于这两种用法的对比,见之后的补充。当我们不想锁住类的对象,只是想同步代码块的时候,可以考虑创建一个对象实例,如下图所示:
升级想法4.0:java里面有些对数变量的操作是原子性的,
Java中的原子操作包括:
1)除long和double之外的基本类型的赋值操作
2)所有引用reference的赋值操作
3)java.concurrent.Atomic.* 包中所有类的一切操作
count++不是原子操作,是3个原子操作组合
1.读取主存中的count值,赋值给一个局部成员变量tmp
2.tmp+1
3.将tmp赋值给count
方法:使用java.util.concurrent.AtomicInteger,详见代码!
- import java.util.concurrent.atomic.AtomicInteger;
- public class Test implements Runnable {
- public static volatile AtomicInteger amount = new AtomicInteger(0);
- public void run() {
- System.out.println(
- Thread.currentThread().getName() + "---------begin--------------" + System.currentTimeMillis());
- for (int i = 0; i < 200000;) {
- amount.incrementAndGet();
- i++;
- }
- System.out.println(
- Thread.currentThread().getName() + "------------end-------------" + System.currentTimeMillis());
- }
- }
- public static void main(String[] args) {
- int num_thread = 50;
- Test test = new Test();
- for (int i = 0; i < num_thread; i++) {
- (new Thread(test)).start();
- }
- try {
- Thread.sleep(10 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(test.amount);
- }
- }
另外补充!synchronized(this) VS synchronize(MyClass.class)
引用国外一个问答网站的精辟回答:
"
MyClass.class
and this
are different things, are different references to different objects.
this
- is the reference to particular this instance of class, and
MyClass.class
- is the reference to MyClass
description object.
This synchronization blocks differs in that the first will synchronize all threads that deal concretely with this instance of MyClass
, and the second one will synchronize all threads independently of which object on which this method was called.
"
翻译过来就是:this同步的是一个具体的对象,所有由这个对象产生的线程在运行同一个方法时都会被阻塞(补充:所有的synchronized标示的方法用也会被阻塞,原因是等同1);MyClass.class同步的是当前类,获取锁的方法将阻塞所有这个类的实例对象,这些对象都无权调用该方法。理解这里的this和MyClass.class非常重要!比如对于上面的例子来说,如果我们的Test extends Thread,如果每次启动线程都是new Test().start(),使用synchronized(this)是无效的(程序中amount要声明为static,属于类),因为每个线程都是一个独立的对象产生的。(注意下面两个等同)。
等同1:
- public void test() {
- synchronized(this) {
- // todo your code
- }
- }
- public synchronized void test() {
- // todo your code
- }
等同2:
如果某方法为类方法,即其修饰符为static,那么synchronized 意味着某个调用此方法的线程当前会拥有该类的锁,只要该线程持续在当前方法内运行,其他线程依然无法获得方法的使用权!
测试代码:
- public class TestSynchronized {
- private InClass inClass = new InClass("name");
- public void test1() {
- synchronized (inClass) {
- int i = 5;
- while (i-- > 0) {
- System.out.println(Thread.currentThread().getName() + " : " + i);
- try {
- Thread.sleep(500);
- } catch (InterruptedException ie) {
- }
- }
- }
- }
- public synchronized void test2() {
- int i = 5;
- while (i-- > 0) {
- System.out.println(Thread.currentThread().getName() + " : " + i);
- try {
- Thread.sleep(500);
- } catch (InterruptedException ie) {
- }
- }
- }
- public void test3() {
- synchronized (TestSynchronized.class) {
- int i = 5;
- while (i-- > 0) {
- System.out.println(Thread.currentThread().getName() + " : " + i);
- try {
- Thread.sleep(500);
- } catch (InterruptedException ie) {
- }
- }
- }
- }
- public void test4() {
- synchronized (this) {
- int i = 5;
- while (i-- > 0) {
- System.out.println(Thread.currentThread().getName() + " : " + i);
- try {
- Thread.sleep(500);
- } catch (InterruptedException ie) {
- }
- }
- }
- }
- public synchronized void test5() {
- int i = 5;
- while (i-- > 0) {
- System.out.println(Thread.currentThread().getName() + " : " + i);
- try {
- Thread.sleep(500);
- } catch (InterruptedException ie) {
- }
- }
- }
- public static void main(String[] args) {
- final TestSynchronized myt1 = new TestSynchronized();
- final TestSynchronized myt2 = new TestSynchronized();
- Thread test1 = new Thread(new Runnable() {
- public void run() {
- myt1.test2();
- }
- }, "test1");
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- // TODO 自动生成的 catch 块
- e.printStackTrace();
- }
- Thread test2 = new Thread(new Runnable() {
- public void run() {
- myt1.test5();
- }
- }, "test2");
- test1.start();
- test2.start();
- }
- class InClass {
- public InClass(String name) {
- // TODO 自动生成的构造函数存根
- }
- }
- }
测试会发现,synchronized(this)会锁住代码块本身的方法、synchronized标示的方法和其它synchroized(this)的代码块;而synchronized(MyClass.class)只能锁住不同对象对应的这一个方法块儿!其他方法(即便是同步的)不会被锁住!
纸上得来终觉浅,还盼诸君勤实践啊!!!啊啊啊啊啊啊!!!!!
参考:synchronize类锁和对象锁详解;深入理解java中的synchronized关键字;java中volatile关键字的含义(这个的评论要看一下)
-------------------------------------- 我 是 华 丽 的 分 割 线 ------------------------------------------
--->>>原因找到了!
关键在于amount is an instance of Integer,synchronized锁的是amount这个对象,但是for循环中amount++这个操作会使得amount这个对象发生变化,这个通过hashcode可以看出来,对于instance of Integer,amount.intValue()==amount.hashcode().所以当这个对象变了之后,之前对象的锁自然就没用了,其它线程开始竞争新对象的锁,由此造成了这样的结果。
--->>>关于synchronized一点补充
synchronized的实现机制需要参照jvm,了解的知识比较多。synchronized(syncObject)类似于给某个对象加锁,{\\code}代码块里面的东西在执行之前都需要获取这把锁,运行完之后马上释放锁。
一个简单的synchronized多线程问题、梳理与思考的更多相关文章
- [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程
一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...
- 一个简单的Python多线程
首先对一段普通的代码进行测试 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import time def RunThrea ...
- Java网络与多线程系列之1:实现一个简单的对象池
前言 为什么要从对象池开始呢,先从一个网络IO操作的demo说起 比如下面这段代码,显而易见已经在代码中使用了一个固定大小的线程池,所以现在的重点在实现Runnble接口的匿名对象上,这个对象每次创建 ...
- 一个简单的多线程Python爬虫(一)
一个简单的多线程Python爬虫 最近想要抓取拉勾网的数据,最开始是使用Scrapy的,但是遇到了下面两个问题: 前端页面是用JS模板引擎生成的 接口主要是用POST提交参数的 目前不会处理使用JS模 ...
- Java Tread多线程(0)一个简单的多线程实例
作者 : 卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39341887 本文演示,一个简单的多线程实例,并简单分析一下线程. 编程多 ...
- C语言多线程的一个简单例子
多线程的一个简单例子: #include <stdio.h> #include <stdlib.h> #include <string.h> #include &l ...
- Qt5.9一个简单的多线程实例(类QThread)(第一种方法)
Qt开启多线程,主要用到类QThread.有两种方法,第一种用一个类继承QThread,然后重新改写虚函数run().当要开启新线程时,只需要实例该类,然后调用函数start(),就可以开启一条多线程 ...
- [MFC] 梳理一个简单的图片处理桌面软件中用到的MFC控件技巧
前言 前些天应好友之拖,帮忙设计一个简单的图像处理的小软件.朋友把核心算法封装好了,但是是用openCV类似于console的编程环境,要我在此基础上改成MFC桌面程序.下图是做成之后的效果: 我是 ...
- 一个简单的HTTP服务器(多线程)
为了更好的了解HTTP协议, 特意谢了一个简单HTTP服务器, 代码只有400行. 因为很简单, 所以效率也不怎么高, 而且支持的特性也不多, 不过也可以运行, 性能跟Apache差不多. ===== ...
随机推荐
- wampserver修改mysql默认字符集
[client] default-character-set=utf8 [mysqld]character_set_server = utf8 重启服务
- python---tuple元祖
ython的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. tup1 = ('physics', 'c ...
- android-8~23 View.java - dispatchTouchEvent源码
android-8 /** * Pass the touch screen motion event down to the target view, or this * view if it is ...
- npoi本地文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...
- 注释驱动的 Spring cache 缓存介绍
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- js 毫秒转日期(yy-MM-dd hh:mm:ss)
var seconds = "133343241342";//毫秒数 var objtime = new Date();//声明一个日期内建函数 objtime.setTime(s ...
- 实用的Portraiture滤镜磨皮教程
滤镜可以快速地进行人物皮肤美化处理,Portraiture滤镜可以将皮肤柔化,消除多余的斑点,在磨皮后复制细节保留较多的通道到图层面板,用高反差保留滤镜提取细节,再更改图层混合模式即可以得到漂亮的肤色 ...
- How to pronounce symbols on keyboard
Refefrence: http://answers.yahoo.com/question/index?qid=20100607151104AAtQxhc ~ “tilde” or “tweedle” ...
- 小结一下:javascript 金额计算
今天在项目中开发一个计算金额的功能,开始我是这样做的: eg: var amount += parseFloat( 0.01+0.02); 结果:0.0300000000000000001 为什么呢? ...
- oracle 查询执行过的SQL语句
SELECT * FROM v$sqlarea t WHERE t.FIRST_LOAD_TIME between '2016-12-23/16:03:00' and '2016-12-23/16:0 ...