把java基础撸一边,从简单的开始。java
线程部分:缓存
存在都是有理由的。若是没有线程咱们的程序只能是下图这个样子,想象一个下假若有1千万个请求,每一个请求1秒,这得请求多长时间
bash
若是不止一个窗口处理事情,这个时候,线程的优势就体现出来了,这样执行完这么多请求就除4了多线程
有点是会体现出来,但同时也暴露出了多线程的不足。一个CPU只能运行一个线程。四核也就是能够运行4个线程,平时看这开100个线程也没事,一下就执行完了,可是这些CPU运行的时间片断过短,执行快因此看上去也没事,可是若是超过了必定范畴也会出问题,(这不是本章的重点),还有就是数据容易成为脏数据,若是多个线程去修改同一个int,那么这个字符串最终是什么样呢?还有各类抢占资源,死锁...等等。jvm
本章重点说的是脏数据问题。其余的后续更新ide
实例,多线程下脏数据的出现:spa
public class Demo21 extends Thread{
private int va = 1;
@Override
public void run() {
for (int i = 0 ; i < 100 ; i++){
va ++ ;
System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
}
}
public static void main(String[] age){
Demo21 demo21 = new Demo21();
demo21.start();
Demo21 demo22 = new Demo21();
demo22.start();
Demo21 demo33 = new Demo21();
demo33.start();
}
}复制代码
三个线程开启,对va进行自加1。按照理想状态,是1 2 3 ...300操作系统
可是结果是什么样呢?.net
Thread Name :Thread-0va :2
Thread Name :Thread-2va :2
Thread Name :Thread-1va :2
Thread Name :Thread-2va :3
Thread Name :Thread-0va :3
复制代码
三个线程 同时打印2。明明va++很短,执行很快为何仍是会出现这个状况。线程
简单看一下jvm的运行空间是什么回事,这里看两个区域,线程共享区,线程独占区。常量是会放到线程共享区的,也就是说没个线程均可以拿到这个值,而线程独占区,里面的数据只能够被当前线程享用(线程之间的数据通讯另说)。这样就能够了解到为何会出现这个状况。
1:数据不是能够立刻写的,从地中中读到数据进入CPU缓存,CPU再作修改,修改完以后在给到主存中
2:A线程修改数据B线程并不知道。
线程一个危险就是这个脏数据,破坏了数据的一致性 。解决这些方法java中提供了不少操做
synchronize,AtomicIntege,LongAdder
此次主要介绍synchronize
public class Demo21 implements Runnable{
private int va = 1;
public void get() {
synchronized (Demo21.class){
va++;
System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
}
}
@Override
public void run() {
for (int i = 0 ; i < 100 ; i++){
get();
}
}
public static void main(String[] age){
Demo21 demo21 = new Demo21();
Thread thread = new Thread(demo21);
Thread thread1 = new Thread(demo21);
Thread thread2 = new Thread(demo21);
thread.start();
thread1.start();
thread2.start();
}
}复制代码
打印结果
Thread Name :Thread-0va :2
Thread Name :Thread-0va :3
Thread Name :Thread-0va :4
Thread Name :Thread-0va :5
Thread Name :Thread-1va :6
Thread Name :Thread-0va :7
Thread Name :Thread-0va :8
Thread Name :Thread-0va :9
Thread Name :Thread-0va :10
复制代码
能够见到加了这个关键字就能够顺序执行。
参考一下java synchronize原理总结,对synchronize作一个初步了解
synchronize的底层是使用操做系统的mutex lock实现
内存可见性:一个线程对共享变量值的修改,可以及时被其余线程看到。
操做原子性:持有同一个锁的两个同步快只能串行进入
synchronize保证 1,2,3,4线程执行synchronize修饰包括的程序代码块中,是保证进入的只有一个线程。synchronize能够理解为一个只容许一个线程进入的大门,进来一个就用它持有的锁给锁住,防止其余线程进入,当代码块执行完毕以后,再开启。
java是面向对象的语言,synchronize用的锁也是存在java对象头里。
synchronized有三种使用方式:
1:修饰实例方法
2:修饰静态方法
3:修饰代码块
修饰实例方法:
public class A {
public void A(){
System.out.println("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void B(){
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}复制代码
public class Demo21 {
public static void main(String[] age){
A a = new A();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.A();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.B();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
}
}复制代码
若是没有synchronize修饰
A
B
1001
1001
复制代码
是没有串行执行的
public synchronized void A()
public synchronized void B()复制代码
修饰实例方法以后
A
1001
B
2000
复制代码
能够看到是串行执行,有锁的效果
public class Demo21 {
public static void main(String[] age){
A a = new A();
A a1 = new A();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a.A();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
long l = System.currentTimeMillis();
a1.B();
long l2 = System.currentTimeMillis() - l;
System.out.println(l2);
}
}).start();
}
}复制代码
若是是不一样的实例执行A B方法
A
B
1001
1001
复制代码
这样就能够知道,修饰非静态实例方法的锁,就是它的实例对象
修饰静态方法:
public static synchronized void B()
public static synchronized void A()
复制代码
修饰静态方法以后
A
B
1000
2000
复制代码
建立两个实例,也是串行执行,这样能够证实是,当前类加锁,进入同步代码块钱要获取当前类对象的锁
修饰代码块
public void A(){
synchronized (A.class){
System.out.println("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void B(){
synchronized (A.class) {
System.out.println("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}复制代码
代码执行后
A
B
1001
2000
复制代码
在synchronize(对象.class)这个对象就是这个同步方法的锁了
这里就是对象synchronize作了一下简单的认识,其实还有不少复杂的东西,要了解synchronize还须要知道jvm,操做系统等等。但能力不足,就介绍到这里。
推荐三篇文章: