指进程中的多个路径同时执行,主要目的是提高程序效率。
【举个栗子】:
打开网易云音乐,可以理解为一个进程,然后点开一首歌曲,这是一个线程,然后在播放歌曲的同时,可以在下边评论,这就是两个线程。
多线程是针对单核CPU的,也就是并发。
多核CPU的多个核心同时运算称为并行。
多线程的本质是CPU时间片的快速切换,当并发操作次数很大时,可以忽略掉创建线程和线程切换的开销,但是如果并发量很小,多线程就显得多此一举了。
在晴朗早晨,和朋友一边散步一边聊天.....
CPU交替执行三件事,但切换速度很快,感觉上就是在同时进行......
class ThreadWalk extends Thread
{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("Walk。。。。。");
}
}
}
class ThreadTalk extends Thread
{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Talking........");
}
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("Sunshine.....");
}
ThreadWalk threadWalk = new ThreadWalk();
threadWalk.start();
ThreadTalk threadTalk = new ThreadTalk();
threadTalk.start();
}
}
运行结果:
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Sunshine.....
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Walk。。。。。
Talking........
Talking........
Talking........
Talking........
Talking........
Talking........
Talking........
Talking........
Talking........
Talking........
Process finished with exit code 0
可继承多个接口,弥补单继承的不足
class MyRunnable1 implements Runnable
{
int i=10;
public void run()
{
for(;i>0;)
{
System.out.printf("day"+"\n");
i--;
}
}
}
class MyRunnable2 implements Runnable
{
int j=10;
public void run()
{
for(;j>0;)
{
System.out.printf("night"+"\n");
j--;
}
}
}
public class Main
{
public static void main(String []args)
{
MyRunnable1 runnable1 = new MyRunnable1();
MyRunnable2 runnable2 = new MyRunnable2();
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
for(int k=0;k<10;k++)
{
System.out.printf("main thread..."+"\n");
}
}
}
运行结果:
main thread...
main thread...
main thread...
main thread...
main thread...
night
night
night
night
night
night
night
night
night
night
day
day
day
day
day
day
day
day
day
day
main thread...
main thread...
main thread...
main thread...
main thread...
public class Main
{
public static void main(String []args)
{
Thread thread = new Thread(
new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("no Name thread。。。。");
}
}
}
);
thread.start();
for(int k=0;k<10;k++)
{
System.out.printf("main thread..."+"\n");
}
}
}
运行结果:
main thread...
main thread...
main thread...
main thread...
main thread...
main thread...
main thread...
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
main thread...
main thread...
main thread...
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
no Name thread。。。。
public class CreatThreadDemo4 implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CreatThreadDemo4 demo4 = new CreatThreadDemo4();
FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最终实现的是runnable接口
Thread thread = new Thread(task);
thread.start();
System.out.println("我可以在这里做点别的业务逻辑...因为FutureTask是提前完成任务");
//拿出线程执行的返回值
Integer result = task.get();
System.out.println("线程中运算的结果为:"+result);
}
//重写Callable接口的call方法
@Override
public Object call() throws Exception {
int result = 1;
System.out.println("业务逻辑计算中...");
Thread.sleep(3000);
return result;
}
}
public class CreatThreadDemo6 {
public static void main(String[] args) {
//创建一个具有10个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
long threadpoolUseTime = System.currentTimeMillis();
for (int i = 0;i<10;i++){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行了...");
}
});
}
long threadpoolUseTime1 = System.currentTimeMillis();
System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
//销毁线程池
threadPool.shutdown();
threadpoolUseTime = System.currentTimeMillis();
}
}
public class CreatThreadDemo5 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时器线程执行了...");
}
},0,1000); //延迟0,周期1s
}
}
public class CreatThreadDemo7 {
public static void main(String[] args) {
List<Integer> values = Arrays.asList(10,20,30,40);
//parallel 平行的,并行的
int result = values.parallelStream().mapToInt(p -> p*2).sum();
System.out.println(result);
//怎么证明它是并发处理呢
values.parallelStream().forEach(p-> System.out.println(p));
}
}
主线程:main方法所在线程
用户线程(子线程):main方法中创建的子线程
守护线程:和main方法一起销毁的线程,比如说GC线程
非守护线程:main方法销毁依然执行的线程
thread.setDaemon(true);可设置线程为守护线程
class Main
{
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <10 ; i++) {
System.out.println("子线程...");
}
}
});
// thread.setDaemon(true);
thread.start();
for (int i = 0; i <10 ; i++) {
System.out.println("main线程.....");
}
System.out.println("主线程销毁....");
}
}
在线程中调用另一个线程的join方法会暂时停止当前线程,将CPU资源给另一个线程先使用。
class Main
{
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("子线程...");
}
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <10 ; i++) {
System.out.println("main线程.....");
}
System.out.println("主线程销毁....");
}
}
子线程...
子线程...
子线程...
子线程...
子线程...
子线程...
子线程...
子线程...
子线程...
子线程...
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
main线程.....
主线程销毁....
当多个线程同时共享,同一个全局变量或静态变量,进行写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是进行读操作是不会发生数据冲突问题。
本质上是数据一致性问题。
经典火车售票问题
class ThreadDemo implements Runnable
{
private static int count = 100;
//非静态同步方法
public synchronized void sale()
{
if(count>0)
{
count--;
System.out.println(Thread.currentThread().getName()+"剩余票数"+count);
}
}
@Override
public void run() {
while(count>0)
{
sale();
}
}
}
public class Main{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
Thread t1 = new Thread(threadDemo,"Window1");
Thread t2 = new Thread(threadDemo,"WIndow2");
t1.start();
t2.start();
}
}
class ThreadDemo implements Runnable
{
private static int count = 100;
//静态同步方法
public synchronized static void sale()
{
if(count>0)
{
count--;
System.out.println(Thread.currentThread().getName()+"剩余票数"+count);
}
}
@Override
public void run() {
while(count>0)
{
sale();
}
}
}
public class Main{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
Thread t1 = new Thread(threadDemo,"Window1");
Thread t2 = new Thread(threadDemo,"WIndow2");
t1.start();
t2.start();
}
}
class ThreadDemo implements Runnable
{
private static int count = 100;
//同步代码块
public void sale()
{
synchronized(this)
{
if(count>0)
{
count--;
System.out.println(Thread.currentThread().getName()+"剩余票数"+count);
}
}
}
@Override
public void run() {
while(count>0)
{
sale();
}
}
}
public class Main{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
Thread t1 = new Thread(threadDemo,"Window1");
Thread t2 = new Thread(threadDemo,"WIndow2");
t1.start();
t2.start();
}
}
前者同步在对象层面上,后者同步在类的层面上
前者在方法上,后者在方法内部。范围不同。
通常来说,范围越大,性能越差。
同步中嵌套同步,导致锁无法释放。
有两个线程并行执行,当线程1拿到了obj1锁,线程2拿到了obj2锁,就会出现互相等待的情况。
线程1拿着obj1等待obj2,线程2拿着obj2等着obj1.
class ThreadDemo implements Runnable {
private static int count = 100;
private Object obj1 = new Object();
private Object obj2 = new Object();
public void sale() {
synchronized (obj1) {
synchronized (obj2) {
if (count > 0) {
count--;
System.out.println(Thread.currentThread().getName() + "剩余票数" + count);
}
}
}
}
@Override
public void run() {
synchronized (obj2) {
synchronized (obj2) {
sale();
}
}
}
}
public class Main{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
Thread t1 = new Thread(threadDemo,"Window1");
Thread t2 = new Thread(threadDemo,"WIndow2");
t1.start();
t2.start();
}
}
Java内存模型(JMM)主要目标是定义程序中共享变量(线程共享)的访问规则。
JMM规定线程之间的共享变量存储在主内存中,每个线程都有一个本地内存(工作内存),本地内存存储了共享变量的副本。
volatile是一种轻量级的同步机制,可以保证可见性【及时将修改的变量刷新到主内存中】,但不能保证原子性,并且禁止重排序。
应用场景:全局共享变量
Volatile的可见性:一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。
JMM关于synchronized的两条规定:
(注意:加锁与解锁需要是同一把锁)
通过以上两点,可以看到synchronized能够实现可见性。同时,由于synchronized具有同步锁,所以它也具有原子性
Synchronized同时具有原子性和可见性,但效率低
Volatile仅具有可见性,但效率高
重排序是指CPU对代码的优化,但是不会对有依赖关系代码做重排序。
比如说
int x = 3; -----1
int y = 5; -----2
int z = x+y; -----3
1和2的执行顺序可能会发生改变,但一定在3之前。
【举个栗子】
定义两个静态变量,创建一个读线程,一个写线程。
如果是按照顺序执行的话,读线程会读到从1到5连续或者相同的数字。
public class Main {
private static int a=0;
private static int b=0;
public static void main(String[] args) {
Thread t1_write = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true)
{
a = 1; // 这里可能发生重排序
b = 1; // 这里可能发生重排序
a = 2; // 这里可能发生重排序
b = 2; // 这里可能发生重排序
a = 3; // 这里可能发生重排序
b = 3; // 这里可能发生重排序
a = 4; // 这里可能发生重排序
b = 4; // 这里可能发生重排序
a = 5; // 这里可能发生重排序
b = 5; // 这里可能发生重排序
a = 4; // 这里可能发生重排序
b = 4; // 这里可能发生重排序
a = 3; // 这里可能发生重排序
b = 3; // 这里可能发生重排序
a = 2; // 这里可能发生重排序
b = 2; // 这里可能发生重排序
}
}
});
Thread t2_read = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(true)
{
System.out.println("a="+a);
System.out.println("b="+b);
System.out.println("=========================");
}
}
});
t1_write.start();
t2_read.start();
}
}
运行结果有的连续,有的并不连续,说明在执行读线程的时候,CPU进行了重排序。但是重排序只是一种情况。也有可能a=2时,执行读线程,输出b的时候a仍在执行,这种情况也会导致不连续。
如何用代码验证重排序目前还没有想出可行的方法,欢迎小伙伴们提出解决方法。^_^