马老师多线程视频学习总结(好记性不如烂笔头)。
继承Thread类和实现Runnable接口
package com.test.thread.msb;
public class T1 {
public static void main(String[] args) {
new Thread(new MyThread1()).start();
new Thread(new MyThread2()).start();
}
// 实现Runnable接口
static class MyThread1 implements Runnable {
@Override
public void run() {
System.out.println("I am runnable, i am start..." + Thread.currentThread().getName());
}
}
// 继承Thread类
static class MyThread2 extends Thread {
@Override
public void run() {
System.out.println("I am thread, i am start..." + Thread.currentThread().getName());
}
}
}
程序执行结果:
使用继承Thread类和实现Runnable接口的关系和区别:
当程序不需要修改和增强Thread类中的方法时,建议使用实现Runnable接口的方式来创建线程。
当多个数据同时操作同一个数据时,可能会产生线程不安全的问题。
package com.wz.code.test.com.wz.code.test.thread;
import java.util.ArrayList;
import java.util.List;
public class T2 {
private int count = 0;
void m() {
for (int i = 0; i < 100000; i++)
count++;
}
public static void main(String[] args) {
T2 t2 = new T2();
long start = System.currentTimeMillis();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t2::m, "threads:" + i));
}
threads.forEach((o)->o.start());
// 等待线程执行完执行主线程
threads.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t2.count);
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("used time: " + time);
}
}
程序执行结果:
我们发现结果并不是100000。这就是线程不安全问题。
java的线程抽象内存模型中定义了每个线程都有一份自己的私有内存,里面存放自己私有的数据,其他线程不能直接访问,而一些共享数据则存在主内存中,供所有线程进行访问。
上图中,如果线程A和线程B要进行通信,就要经过主内存,比如线程B要获取线程A修改后的共享变量的值,要经过下面两步:
线程A修改自己的共享变量副本,并刷新到了主内存中。
线程B读取主内存中被A更新过的共享变量的值,同步到自己的共享变量副本中。
java多线程中的原子性、可见性、有序性
原子性:是指线程的多个操作是一个整体,不能被分割,要么就不执行,要么就全部执行完,中间不能被打断。
可见性:是指线程之间的可见性,就是一个线程修改后的结果,其他的线程能够立马知道。
有序性:为了提高执行效率,java中的编译器和处理器可以对指令进行重新排序,重新排序会影响多线程并发的正确性,有序性就是要保证不进行重新排序(保证线程操作的执行顺序)。
其实volatile关键字的作用就是保证了可见性和有序性(不保证原子性),如果一个共享变量被volatile关键字修饰,那么如果一个线程修改了这个共享变量后,其他线程是立马可知的。为什么是这样的呢?比如,线程A修改了自己的共享变量副本,这时如果该共享变量没有被volatile修饰,那么本次修改不一定会马上将修改结果刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是没有被A修改之前的值。如果该共享变量被volatile修饰了,那么本次修改结果会强制立刻刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是被A修改之后的值了。
volatile能禁止指令重新排序,在指令重排序优化时,在volatile变量之前的指令不能在volatile之后执行,在volatile之后的指令也不能在volatile之前执行,所以它保证了有序性。
synchronized提供了同步锁的概念,被synchronized修饰的代码段可以防止被多个线程同时执行,必须一个线程把synchronized修饰的代码段都执行完毕了,其他的线程才能开始执行这段代码。
因为synchronized保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作了,那么线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。
package com.wz.code.test.thread;
public class T {
public synchronized void m1() {
System.out.println("m1 start...");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("m1 end...");
}
public void m2() {
System.out.println("m2 start...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("m2 end...");
}
public static void main(String[] args) {
T t = new T();
new Thread(t::m1, "t1").start();
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
使用volatile 和synchronized
package com.wz.code.test.thread;
import java.util.ArrayList;
import java.util.List;
public class VolatileTest {
volatile int count;
synchronized void m() {
for (int i = 0; i < 100000; i++)
count++;
}
public static void main(String[] args) {
VolatileTest vt = new VolatileTest();
long start = System.currentTimeMillis();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(vt::m, "thread:" + i));
}
threads.forEach((o) -> o.start()); // JDK1.8新特性
threads.forEach((o) -> { // JDK1.8新特性
try {
o.join(); // 等线程执行完毕之后才执行主线程main
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(vt.count);
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("used time: " + time);
}
}
package com.wz.code.test.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class T3 {
AtomicInteger count = new AtomicInteger(0);
void m() {
for (int i = 0; i < 100000; i++)
count.incrementAndGet();
}
public static void main(String[] args) {
T3 t3 = new T3();
long start = System.currentTimeMillis();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t3::m, "threads:" + i));
}
threads.forEach((o)->o.start());
// 等待线程执行完执行主线程
threads.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t3.count);
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("used time: " + time);
}
}