0%

在计算机的世界,都是0和1 ,利用这个0和1组成了计算机的基础,数字是如何在计算机中表示的?二进制到底是什么?

数字怎么表示

在刚开始学编程的时候,有几种常见的数据基础的数据类型占用几个字节,如int 占16个字节(不同的语言体系不同,这里以C语言为例)。

一个字节可以表示两个数字0和1 ,占16个字节就可以用2^16个数字。

为了能够表示负数,单独保留一个字节作为符号位, 所以int的整型的范围是从-32768到32767 .

二进制的换算

十进制的数字是逢十进一,二进制很简单是逢二进一,比如十进制:3+9=12. 在二进制中:1+1=10.

计算二进制的方法与十进制也相同,比如在十进制中想取个十百的数字,可以分别除10的倍数。

1
2
3
4
5
6
7
8
9
10
比如取456的各位的数:       

456/10=45余6 ,

45/10=4 余5

4/10= 0余4

最终倒序返回时456

换成二进制的方法与十进制相同,唯一的区别是负数的表示方法不同。

阅读全文 »

前几天写了一个Java发送邮件的帮助类i,可以发送QQ和163的邮箱,也可以发送附件,写个一个主要的方法,其他的可以自己封装。代码如下:

引入pom:

1
2
3
4
5
6
<dependency>
<groupId>Javax.mail</groupId>
<artifactId>Javax.mail-api</artifactId>
<version>1.6.2</version>
</dependency>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package com.pay.utils;

import com.pay.utils.enums.MailType;
import org.springframework.util.StringUtils;

import Javax.activation.*;
import Javax.mail.*;
import Javax.mail.internet.*;
import Javax.mail.util.ByteArrayDataSource;
import Java.io.*;
import Java.util.HashMap;
import Java.util.Map;
import Java.util.Properties;

public class MailSender {
private MailType mailType;
private String userName;
private String passWord;
private Properties properties;


public MailSender(MailType mailType, String userName, String passWord) {
this.mailType = mailType;
this.userName = userName;
this.passWord = passWord;
this.properties = getProperties();
}


public boolean sender(String recivers, String cc, String mailTitle, String mailContent, boolean isHtml, Map<String, byte[]> mapFile) throws MessagingException, IOException {
Session session = Session.getInstance(properties);
//2.通过session获取Transport对象(发送邮件的核心API)
Transport ts = session.getTransport();
//3.通过邮件用户名密码链接
ts.connect(properties.getProperty("mail.host"), userName, this.passWord);
//4.创建邮件
MimeMessage mm = new MimeMessage(session);
//设置发件人
mm.setFrom(new InternetAddress(userName));
Address[] address = new InternetAddress().parse(recivers);
mm.setRecipients(Message.RecipientType.TO, address);

//设置抄送人
if (!StringUtils.isEmpty(cc)) {
mm.setRecipient(Message.RecipientType.CC, new InternetAddress(cc));
}
mm.setSubject(mailTitle);
if (!isHtml) {
mailContent = String.format("<pre>%s</pre>", mailContent);
}
// mm.setContent(mailContent, "text/html;charset=utf-8");
// 创建多重消息
Multipart multipart = new MimeMultipart();

BodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(mailContent, "text/html;charset=utf-8");
multipart.addBodyPart(bodyPart);
if (mapFile != null) {
MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
mc.addMailcap("text/html;; x-Java-content-handler=com.sun.mail.handlers.text_html");
mc.addMailcap("text/xml;; x-Java-content-handler=com.sun.mail.handlers.text_xml");
mc.addMailcap("text/plain;; x-Java-content-handler=com.sun.mail.handlers.text_plain");
mc.addMailcap("multipart/*;; x-Java-content-handler=com.sun.mail.handlers.multipart_mixed");
mc.addMailcap("message/rfc822;; x-Java-content-handler=com.sun.mail.handlers.message_rfc822");
CommandMap.setDefaultCommandMap(mc);

for (Map.Entry<String, byte[]> map : mapFile.entrySet()) {
BodyPart messageBodyPart = new MimeBodyPart();
InputStream inputStream = new ByteArrayInputStream(map.getValue());
DataSource source = new ByteArrayDataSource(inputStream, "application/txt");
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(MimeUtility.encodeText(map.getKey()));
multipart.addBodyPart(messageBodyPart);
}

mm.setContent(multipart);
}
//5.发送电子邮件
ts.sendMessage(mm, mm.getAllRecipients());
return true;
}

private Properties getProperties() {
if (this.mailType.equals(MailType.m163)) {
Properties prop = new Properties();
prop.put("mail.host", "smtp.163.com");
prop.put("mail.transport.protocol", "smtp");
prop.put("mail.smtp.auth", true);
return prop;
}
if (this.mailType.equals(MailType.qq)) {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
prop.setProperty("mail.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth", "true");
prop.setProperty("mail.smtp.socketFactory.class", "Javax.net.ssl.SSLSocketFactory");
prop.setProperty("mail.smtp.port", "465");
prop.setProperty("mail.smtp.socketFactory.port", "465");
return prop;
}
return null;
}

}


```

``` Java
public enum MailType {
m163, qq
}


阅读全文 »

上次大概说了CountDownLatch的使用,今天说下实现的原理,CountDownLatch的使用效果和Join差不多,实现起来也比较简单。

大体的思路就是一个死循环阻塞,等到某个条件满足后就跳出循环,继续执行后面的代码。执行逻辑如下:

CountDownLatch的原理

源码分析


我们下面分析下CountDownLatch的源码:

创建CountDownLatch对象


  public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

创建CountDownLatch 对象很简单,就是创建一个Sync对象。 Sync的对应的代码

阅读全文 »

单例模式

在编程中,单例模式是我们常用的一种设计模式,功能是保证在整个系统只用一个该对象的对象,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class Singleton {
private static Singleton singleton;

private Singleton() {
}

public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
return singleton;
}
return singleton;
}
}

上面的代码我们知道并不是线程安全的,在多线程环境下,容易造成创建多个对象。 测试代码如下:

1
2
3
4
5
6
7
8
9
10
11

@Test
public void testSingleton() throws InterruptedException {
for (int i=0;i<10;i++){
new Thread(()->{
Singleton.getInstance();
}).start();
}
Thread.currentThread().join();
}

运行结果如下:

1
2
3
4
5
6
7
创建对象
创建对象
创建对象
创建对象
创建对象
创建对象
创建对象

解决方案


对于上面的问题解决的方法有很多,比如使用加锁的方式,double检测的方式,为了验证最有方案我们把代码修改下:

阅读全文 »

在Java多线程中有一对配合使用过的两个方法,来实现线程间通信的功能—lock和wait, 由于这个需要获得锁,所以必须结合synchronized一起使用。首先我们先看一个例子:


  public class LockWait {

    static volatile List<String> itemContainer = new ArrayList<>();
    static Object obj = new Object();

    public static void main(String[] args) {
        Thread th1 = new Thread(() -> {
            synchronized (obj) {
                for (int i = 0; i < 10; i++) {
                    System.out.println("th1添加元素");
                    itemContainer.add(String.valueOf(i));
                    if (itemContainer.size() == 5) {
                        System.out.println("th1线程发出通知");
                        obj.notify();
                    }
                }
            }
        });

        Thread th2 = new Thread(() -> {
            synchronized (obj) {
                System.out.println("进入th2线程");
                if (itemContainer.size() != 5) {
                    try {
                        System.out.println("th2线程开始等待");
                        obj.wait();
                        System.out.println("th2线程等待结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("th2线程结束");
                }
            }

        });

        th2.start();
        th1.start();
    }
}

输出结果如下:

进入th2线程
th2线程开始等待
th1添加元素
th1添加元素
th1添加元素
th1添加元素
th1添加元素
th1线程发出通知
th1添加元素
th1添加元素
th1添加元素
th1添加元素
th1添加元素
th2线程等待结束
th2线程结束

具体运行逻辑如下:

Java多线程通信lock和wait

总结上面的运行结果,th2在wait的时候,th1可以持有锁。说明wait是释放锁,而notify不释放锁。

这样也就带来了一个弊端,无法实时的得到结果,就是说当List达到我们想要的结果的时候,th1线程一直还在持有锁,导致th2无法执行。

有没有更好办法呢?在Java中提供了一个CountDownLatch类:


public class CountDownLatchTest {

    public static void main(String[] args) {
        final List<String> itemContainer = new ArrayList<>();
        final CountDownLatch countDownLanch = new CountDownLatch(1);
        Thread th1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("th1添加元素");
                    itemContainer.add(String.valueOf(i));
                    if (itemContainer.size() == 5) {
                        System.out.println("th1线程发出通知");
                        countDownLanch.countDown();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });

        Thread th2 = new Thread(() -> {
            System.out.println("进入th2线程");
            if (itemContainer.size() != 5) {
                try {
                    System.out.println("th2线程开始等待");
                    countDownLanch.await();
                    System.out.println("th2线程等待结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("th2线程结束");
            }

        });

        th2.start();

        th1.start();
    }
}
阅读全文 »

在Java多线程中,有一个特殊的关键字volatile,这个通常成为一个“轻量级锁”,下面我们就来深入的了解这个关键的作用和原理。

线程的内存备份

首先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class VolatileThread extends Thread {
private boolean isRuning=true;
private void setRuning(boolean runing){
this.isRuning=runing;
}

public void run(){
System.out.println("进入Run方法");
while (isRuning){

}
System.out.println("线程结束");
}

public static void main(String[] args) throws InterruptedException {
VolatileThread volatileThread = new VolatileThread();
volatileThread.start();
Thread.sleep(3000);
volatileThread.setRuning(false);
System.out.println("runing设置成false,让线程停止");
Thread.sleep(1000);
System.out.println(volatileThread.isRuning);
}
}

在上面的代码并没有打印出“线程结束”的信息,因为我在主线程更改了isRuning 的值,并没有影响到线程中的数据。

产生这个的原因是因为JDK在创建线程的时候,都会从主内存中拷贝一份数据,所以线程的读取的变量的具有一定延迟

深入volatile关键字

使用volatile

对上面的代码进行修改,把isRuning变量使用volatile 关键字修饰,这样我们就能看到线程能够正常的停止了。下面我们总结下volatile的作用

1
2
3

如果变量被volatile关键字修饰, 则当变量改变的时候强制线程从主内存中读取和写入变量

阅读全文 »

CPU的构成

我们知道CPU是芯片的集合,主要成分是硅。CPU的最小构成单位是一个PN节点,也就是我们常说的二极管。下面我们就聊一聊什么是二极管

PN节点 (二极管)

PN节点是一个硅晶体进行掺杂,分别在两侧掺入硼和磷,这样的硅晶体具有单项导电性,这样就形成一个PN节点。具体如下图:

CPU运算

由于具有单项导电性的特点,我们就能根据收到的电压变化,来确定输出的结果,我们假设收到高电压是1 ,低电压是0,PN节点的具体表现:

CPU运算

实现基本运算

根据上面的分析,我们可以尝试实现一个与门的电路实现,首先我们要清楚与门的具体逻辑。

有两个输入参数,只有同时为1的时候,才输出1,具体表示如下:

阅读全文 »

在写代码的时候,我们直接在没有编译报错的时候,直接点击运行后,ide会直接把程序的结果输出到控制台上,代码如下:

1
2
3
4
5
6
7
  public static void main(String[] args) { 
int i=17;
int j=5;
int sum=i+j;
System.out.println(sum);
}

这段代码最终的结果是在控制台上面打印出:22,但是这个结果到底是怎么被执行的呢?

CPU能做什么

在硬件的世界里面,只有0和1,就是这么简单的0和1,到底是怎么做加法的呢?

我们知道CPU的功能是执行指令,有三个简单的基本操作:与,非,或三种运算。在加上位的运算一种有5种:&,|,~,<<,>>. 利用这个几个运算如何实现代码中的15+5的运算?

首先,把加法拆解,分成两个部分: 把个位和个位相加,如果有进1的话,就用进1的值十位与另一个十位相加。得到的和在进行相加。

  1. 把15+5进行拆解就是 7+5=12,发现5+5有进位10;
  2. 利用进位的十位与10+10 =20
  3. 再把两个的和相加,20+2=22 ,没有再进位,运算结束。

    根据上面的分析,我们可以使用递归的方法,写出加法的位运算代码如下:

    1
    2
    3
    4
    5
    6
    7
    static int add(int i, int j){
    if(j == 0)
    return i;
    int sum = i ^ j;//得到个位相加
    int carry = (i & j) << 1;//得到进位相加
    return add(sum, carry);
    }

    对这个算法进行封装成一个CPU指令,我们就可以利用二进制进行进行运算。

Java代码最终的编译结果

我们知道Java的代码最终是经过编译器,转换成字节码最终由JVM解释执行,具体过程如下:

阅读全文 »

并发问题

在编程的时候我们经常会碰到并发的问题,如果处理不好很有可能造成业务数据的错误。我们思考,到底什么是并发问题?
简单的来说我们可以把并发问题归纳为:未写入而先读取 带来的问题。

我们用最简单的取钱的模型来描述这个问题:

compare

在①②③④ 这个几个步骤中,①②和③④分别是两个独立的过程,如果执行的顺序是 ①③②④,这样就会带来最终余额为负的现象,这个就是一个简单的并发问题。

我们可以用代码简单的模拟这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

public class AppTest {
private int count = 0;
public static void main(String[] args) {
long ts = System.currentTimeMillis();
AppTest app = new AppTest();
List<Thread> tList = new ArrayList<>(500);

for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(() -> {
for (int m = 0; m < 1000; m++) {
app.count();
}
});
tList.add(thread);
}
//启动线程
for (Thread t : tList) {
t.start();
}
//等待所有线程执行完
for (Thread t : tList) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println(app.count);
long tend = System.currentTimeMillis();
System.out.println(tend-ts);
}

private void count() {
count++;
}
}

上述代码执行结果: 9997990(结果具有不确定性,此次结果就偶然一次结果),耗时:3378

造成这个结果的原因就是,在多线程执行的过程中,count的值还没有来得及写入内存,另一个线程就已经把count的读取,就导致count少一次count++运算。

解决并发

阅读全文 »