把java基础撸一边,从简单的开始。
线程部分:
存在都是有理由的。如果没有线程我们的程序只能是下图这个样子,想象一个下假如有1千万个请求,每个请求1秒,这得请求多长时间
如果不止一个窗口处理事情,这个时候,线程的优点就体现出来了,这样执行完这么多请求就除4了
有点是会体现出来,但同时也暴露出了多线程的不足。一个CPU只能运行一个线程。四核也就是可以运行4个线程,平时看这开100个线程也没事,一下就执行完了,但是这些CPU运行的时间片段太短,执行快所以看上去也没事,但是如果超过了一定范畴也会出问题,(这不是本章的重点),还有就是数据容易成为脏数据,如果多个线程去修改同一个int,那么这个字符串最终是什么样呢?还有各种抢占资源,死锁...等等。
本章重点说的是脏数据问题。其他的后续更新
实例,多线程下脏数据的出现:
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
但是结果是什么样呢?
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,操作系统等等。但能力不足,就介绍到这里。
推荐三篇文章: