Java高并发设计-------------Java并行程序基础------------2
Java并行程序基础
2.1有关线程必须知道的事
进程:系统进行资源分配和调度的基本单位,线程的容器;程序是指令、数据及其组织形式的描述,进程是程序的实体
线程:线程就是轻量级进程,是程序执行的最小单位。
使用多线程而不是多进程去并发程序的设计,是因为线程间的切换和调度的成本远远小于进程!!!!
Idea中全局查找一个类快捷键:ctrl + shift + n
/*
线程状态
*/
public enum State { NEW, //线程创建
RUNNABLE, //线程执行
BLOCKED, //如果线程在执行过程中遇到了synchronized同步块,就会进入到阻塞状态
TIMED_WAITING, //有时间限制的等待
WAITING, //无时间限制的等待
TERMINATED; //执行完成
}
2.2初始线程:线程的基本操作
两种方法:
继承Thread类
public class ThreadInitDemo { public static void main(String[] args) { class ThreadDemo extends Thread{ @Override public void run(){ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } } ThreadDemo thread = new ThreadDemo(); thread.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } }
实现Runnable接口
public class ThreadRunnable implements Runnable { public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } public static void main(String[] args) { new Thread(new ThreadRunnable()).start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } }
经典皮以下
假如直接调用run方法会怎样?
public class ThreadInitDemo { public static void main(String[] args) { class ThreadDemo extends Thread{ @Override public void run(){ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } } ThreadDemo thread = new ThreadDemo();
/*
阿喆请注意!!!!!
*/ thread.run();
for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } }
结果表明:
并没有开启新的线程,还是主线程执行的!
那么,看看start方法的源码吧:
1 public synchronized void start() { 2 /*标志该线程是否启动过,没有启动过才能执行接下来的动作*/ 3 if (threadStatus != 0) 4 throw new IllegalThreadStateException(); 5 group.add(this); //将该线程加入到线程组中 6 boolean started = false; 7 try { 8 start0(); //启动start0方法 9 started = true; //设置是否启动为true,是否与ThreadStatus重合,此处不懂 10 } finally { 11 try { 12 if (!started) { 13 group.threadStartFailed(this); 14 } 15 } catch (Throwable ignore) { 16 } 17 } 18 }
其中:
threadStatus:表示这个线程未被启动
group.add(this):将我们创建的线程加入到线程组中,可见线程组是用数组存储的!!
start0():是一个本地方法,涉及到底层。Java无法直接控制操作系统开辟一条线程,据说Start0()是用c++写的,存储在本地方法库中。c++调用操作系统开辟一条线程,执行我们的任务。
线程执行步骤:Thread.start():调用start0()方法,创建一条线程,执行run方法;所以我们直接调用run方法,并没有开辟新的线程。
谨记!!!!!!!:只能单继承,所以a喆,能不用继承的方式创建线程就不用。
2.2.2终止线程
Thread.stop():被舍弃了哦,说它太暴力了,强行终止,可以看到stop0(),估计直接调用底层,直接杀死咯;这样可能引起一些数据不一致问题。
因为:Thread.stop()方法在结束线程,会直接释放所有的锁,这样可能导致数据不一致。
Stop测试代码如下
代码含义:read线程一直在读;write线程一直在写,但是写了150ms之后就终止了;导致id和name不一致!
public class StopThreadUnsafe { public static User u = new User(); public static class User{ private int id; private String name; public User() { this.id = 0; this.name = "0"; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } public static class ChangeObjectThread extends Thread{ @Override public void run(){ while (true){ synchronized (u){ int v = (int)(System.currentTimeMillis()/1000); u.setId(v); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } u.setName(String.valueOf(v)); } Thread.yield(); } } } public static class ReadObjectThread extends Thread{ @Override public void run(){ while (true){ synchronized (u){ if (u.getId() != Integer.parseInt(u.getName())){ System.out.println(u.toString()); } } Thread.yield(); } } } public static void main(String[] args) throws InterruptedException { new ReadObjectThread().start(); while (true){ Thread t = new ChangeObjectThread(); t.start(); Thread.sleep(150); t.stop(); } } }
将上述代码修改正确:
修改上增加setStopMe()方法,看不懂自己敲一遍就懂咯,就是设置了一个标志位,当写线程stop后,标志为改变,因为写是死循环,所以会检测到标志位改变了,直接退出,并没有时间去修改u。
说句实话,修改main方法中的sleep长短,就还是不安全的。书上会个锤子,害。
但是标志位,是一个保证线程安全的一种方法
public static class ChangeObjectThread extends Thread{ volatile boolean stopMe = false; public void setStopMe(){ stopMe = true; } @Override public void run(){ while (true){ if (stopMe){ System.out.println("我被终结了!!!"); } synchronized (u){ int v = (int)(System.currentTimeMillis()/1000); u.setId(v); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } u.setName(String.valueOf(v)); } Thread.yield(); } } }
1 public static void main(String[] args) throws InterruptedException { 2 3 new ReadObjectThread().start(); 4 while (true){ 5 ChangeObjectThread t = new ChangeObjectThread(); 6 t.start(); 7 Thread.sleep(150); 8 t.stop(); 9 t.setStopMe(); 10 } 11 }
2.2.3线程中断
线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了!以至于目标线程接收到通知后如何处理,则完全由目标线程自行决定。
public void Thread.interrupt() // 中断线程 实例方法!! public boolean Thread.isInterrupted() // 判断是否中断 实例方法!!! public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态 类方法!!!
public class InterruptedDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run(){ while (true){ if (this.isInterrupted()){ System.out.println("15"); break; } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); } }
谨记:调用interrupt()方法后!!!请记得手动的在线程中写中断处理的执行语句
调用Thread.interrupt()方法
判断后,就直接将中断标志清除掉了,不知道有什么用!!!
sleep函数
本地方法,会抛出InterruptedException异常
如果睡眠状态的线程被中断了,就抛出异常!InterruptedException不是运行时异常。
运行时异常????(待补充)
2.2.4 等待(wait)和通知(notify)
所有对象都会有的方法
public final void wait() throws InterruptedException; public final native void notify();
wait
当一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。
例如:在线程A上,调用obj.wait()方法,那么线程A就会停止继续执行,转为等待状态。等到,线程A会一直等到其它线程调用obj.notify()方法位置。这时,object对象俨然成了多个线程之间的通信手段
谨记:wait和notify的方法只能放在同步代码块中使用
执行流程:
- 线程A执行到,object.wait()等待
- 线程A释放锁
- 线程B执行,唤醒object,然后执行完毕,释放锁
- 线程A获得object,执行结束
Thread.sleep()方法可是不会释放锁的哦
2.2.5挂起(suspend)和继续执行(resume)过程
2.2.6等待线程结束(join)和谦让(yeild)
join(有点不太懂的)
public final void join() throws InterruptedException //没有时间限制 public final synchronized void join(long millis) throws InterruptedException //有时间限制(一定时候就直接释放了)
例子:join()
public class JoinDemo1 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ for (int i = 0; i < 100; i++) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "----------------->" + i); } }, "thread"); t.start(); t.join(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } } }
从上面的例子看出,只有当thread中的任务执行完毕,才能执行thread.join之后的代码
例子证实下:
public class JoinDemo1 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } }, "thread"); t.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } t.join(); System.out.println("我好了"); } }
猜想正确!!!别整那些将线程加入到哪里,就是摁等,结束了才能执行接下来的线程
那两个线程都join呢?
public class JoinDemo1 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } }, "thread"); t.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } Thread.currentThread().join(); t.join(); System.out.println("我好了"); } }
上述代码会无限死循环!!!贴join函数源码
join函数:让主线程等待子线程完成才能执行,那么这里是主线程等待主线程就,所以没有意义!
并且,源码显示这还是个死循环。
public class JoinDemo1 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "----------------->" + i); } }, "thread"); t.start(); Thread t2 = new Thread(()->{ for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "----------------->" + i); } }, "thread2"); t2.start(); t.join(); System.out.println("我好了"); } }
当join的线程结束之后才能执行其后面的代码
yield
public static native void yield() //主动让出当前CPU
我已经完成了一些重要工作,我可以休息一下了,你们来工作吧。
2.3volatile与Java内存模型(JMM)
volatile:所有线程都可以看到被volatile修饰的变量作的任何改变
对原子性有很大的帮助,但是对复合性操作的原子性没有帮助
2.4线程组
ThreadGroup:是一个类
创建线程的时候,可以选择加入到线程组当中
new Thread(ThreadGroup, new ThreadGroupName, "T1")
2.5守护线程(Daemon)
垃圾回收线程、JIT线程、、、===========》守护线程
我们的、main线程===========》用户线程,可以将任意一个线程设置为守护线程
当系统中只有守护线程时,就自动退出了,即便此时自定义的线程还在工作,也会退出的。
2.6线程的优先级
public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10;
优先级的范围1-10之间
thread.setPriority(优先级)
2.7线程安全的概念与关键字synchronized
volatile不能保证线程安全
synchronized锁:
指定加锁对象:对给定对象加锁,进入同步代码前需要获得给点对象的锁
直接作用于实例方法:相当于当前实例加锁
直接作用静态方法:相当于对当前类加锁,将进入同步代码前要获得当前类的锁