CPU核心数和线程数的关系
核心数:线程数=1:1 ;使用了超线程技术后—> 1:2
CPU时间片轮转机制
又称RR调度,会导致上下文切换
什么是进程和线程
进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源
线程:CPU调度的最小单位,必须依赖进程而存在。(先有进程再有线程。CPU先将资源分配给进程,然后线程再使用资源)
澄清并行和并发
并行:同一时刻,可以同时处理事情的能力
并发:与单位时间相关,在单位时间内可以处理事情的能力
高并发编程的意义、好处和注意事项
好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
问题:
线程共享资源,存在冲突;
容易导致死锁;
启用太多的线程,就有搞垮机器的可能
public class MyThread extends Thread {
//实现Runnable接口
private static class UseRun implements Runnable{
@Override
public void run() {
System.out.println("I'm runnable implements");
}
}
//实现Callable接口
private static class UseCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I'm callable implements");
return "callable";
}
}
//测试
public static void main(String[] args) throws ExecutionException, InterruptedException {
UseRun useRun = new UseRun();
//实现Runnable接口的类 需要交给线程类Thread去执行
new Thread(userRun).start();
UseCall useCall = new UseCall();
//PS:实现Callable的类无法直接转化为Thread 这就要使用FutureTask类 该类继承了Runnable接口
//相当于将UseCall -> Runnable -> Thread
FutureTask<String> futureTask = new FutureTask<>(userCall);
//执行该线程
new Thread(futureTask).start();
//获取并且打印UserCall的返回值
System.out.println(futureTask.get());
}
}
①线程自然终止:②自然执行完或抛出未处理异常
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
这三种方法只有Thread以及其子类才能够使用
1.继承Thread类创建线程方式 安全中断线程的代码:
public class EndThread {
private static class UseThread extends Thread{
public UseThread(String name){
super(name);
}
//重写Thread中的run方法
@Override
public void run(){
String name = Thread.currentThread().getName();
//判断当前的线程是否被中断
while(! isInterrupted()){
//获取当前运行线程的名字
System.out.println("Thread:" + name + " is run");
}
System.out.println("interrupt flag is : " + isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread("endThread");
endThread.start();
//令主线程休眠2秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(2000);
endThread.interrupt();
}
}
UseThread线程会运行2s 直到2s后main方法执行endThread.interrupt(); 线程中断。
执行结果:
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
interrupt flag is : true
@Override
public void run(){
String name = Thread.currentThread().getName();
//判断当前的线程是否被中断
while(true){
//获取当前运行线程的名字
System.out.println("Thread:" + name + " is run");
System.out.println("interrupt flag is : " + isInterrupted());
}
}
执行结果为:
Thread:endThread is run
interrupt flag is : false
Thread:endThread is run
interrupt flag is : false
Thread:endThread is run
interrupt flag is : false
interrupt flag is : true
Thread:endThread is run
interrupt flag is : true
Thread:endThread is run
interrupt flag is : true
Thread:endThread is run
程序会永远执行下去。 可以看到2s之前中断标志一直为false,2s后运行到主方法的endThread.interrupt();后中断标志变为true 但程序仍然会一直运行,这恰恰说明了 java的线程是协作式的并不是抢占式的。即使发出了中断线程的信号,线程仍然执行,因为线程是否中断最终的决定权是在线程本身。
2.实现Runnable接口方式创建线程 安全中断线程的方法
其实该方法和上面的方法大同小异 只是不能直接使用interrupt()、isInterrupted()、interrupted()这三种方法
public class EndRunnable {
private static class UseRunnable implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();//❤
//判断当前线程是否被中断 使用Thread.currentThread().isInterrupted() 是因为只有Thread类才有这个方法
while (! Thread.currentThread().isInterrupted()){//❤
//获取当前运行线程的名字
System.out.println("Thread:" + name + " is run");
}
System.out.println("interrupt flag is : " + Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
Thread endRunnable = new Thread(new UseRunnable(), "useRunnable");//❤
endRunnable.start();
Thread.sleep(2000);
endRunnable.interrupt();
}
}
public class HasInterruptException {
private static class UseThread extends Thread{
public UseThread(String name){
super(name);
}
//重写Thread中的run方法
@Override
public void run(){
String name = Thread.currentThread().getName();
//判断当前的线程是否被中断
while(! isInterrupted()){
//获取当前运行线程的名字
try {
System.out.println("Thread:" + name + " is run");
//制造中断异常 InterruptedException
Thread.sleep(200);//❤
}catch (InterruptedException e){
//打印标志位
System.out.println("catch ----- interrupt flag is : " + isInterrupted());//❤
e.printStackTrace();
}
System.out.println("after catch ----- interrupt flag is : " + isInterrupted());//❤
}
System.out.println("after while ----- interrupt flag is : " + isInterrupted());//❤
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread("endThread");
endThread.start();
//令主线程休眠2秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(2000);
endThread.interrupt();
}
}
执行过程:线程在前2s会执行10次(在run方法中线程每执行一次会休眠200ms)
第2s时main方法执行到endThread.interrupt();会打断run方法中的休眠 这样就会抛出java.lang.InterruptedException: sleep interrupted异常。
然后会一直执行,永不停止。
执行结果:
Thread:endThread is run
after catch ----- interrupt flag is : false
Thread:endThread is run
catch ----- interrupt flag is : false
//以上为2s前线程运行情况
//线程发生异常
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at pers.amos.concurrent.safeend.HasInterruptException$UseThread.run(HasInterruptException.java:27)
after catch ----- interrupt flag is : false
Thread:endThread is run
after catch ----- interrupt flag is : false
Thread:endThread is run
after catch ----- interrupt flag is : false
可以看到在异常发生后中断标志重置为false 这样线程才能够一直执行
总结:当线程中发生InterruptedException异常时,标志位会被重置为false
如何改进程序,防止这类情况的发生?
在catch中加入语句interrupt(); 对线程进行再次中断即可
try {
System.out.println("Thread:" + name + " is run");
//制造中断异常 InterruptedException
hread.sleep(200);
}catch (InterruptedException e){
interrupt();//❤
//打印标志位
System.out.println("catch ----- interrupt flag is : " + isInterrupted());
e.printStackTrace();
}
改进后的执行结果:
Thread:endThread is run
after catch ----- interrupt flag is : false
Thread:endThread is run
catch ----- interrupt flag is : true
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at pers.amos.concurrent.safeend.HasInterruptException$UseThread.run(HasInterruptException.java:27)
after catch ----- interrupt flag is : true
after while ----- interrupt flag is : true
1.yield和sleep方法的区别
2.深入理解run()和start()
线程对象调用run()和调用start()的区别
public class StartAndRun {
private static class ThreadRun extends Thread{
@Override
public void run() {
int i = 5;
while(i > 0){
System.out.println("我是-" + Thread.currentThread().getName() + "-" + i );
i --;
}
}
}
public static void main(String[] args) {
ThreadRun threadRun = new ThreadRun();
threadRun.setName("amosThread");
//设置优先级
threadRun.setPriority(1);
threadRun.run();
threadRun.start();
}
}
输出结果:
我是-main-5
我是-main-4
我是-main-3
我是-main-2
我是-main-1
我是-amosThread-5
我是-amosThread-4
我是-amosThread-3
我是-amosThread-2
我是-amosThread-1
run()方法由谁去调用它就归属于哪个线程(这里是在主线程里调用的)其实和普通方法的调用是一样的。
start()方法:调用该方法后JVM会把线程类映射成一个线程。
3.线程的优先级:
取值为1~10,缺省为5,但线程的优先级不可靠,不建议作为线程开发时候的手段
//设置优先级
threadRun.setPriority(1);
4.守护线程
和主线程共死,finally不能保证一定执行
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
while (!isInterrupted()) {
System.out.println("Thread:" + Thread.currentThread().getName() + " is run");
}
System.out.println(Thread.currentThread().getName()
+ " interrupt flag is " + isInterrupted());
}
}
1.当endThread 不设置为守护线程时,主线程执行完毕,endThread线程会永远执行下去。
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread();
endThread.start();
//令主线程休眠5毫秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(5);
}
}
2.当endThread线程设置为守护线程时,主线程执行完毕,endThread线程也会停止,这就叫做与主线程共死。
PS:设置守护线程一定要在调用start()方法之前
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread();
//设置线程为守护线程
endThread.setDaemon(true);//❤
endThread.start();
//令主线程休眠5毫秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(5);
}
}
3.守护线程的finally不一定执行
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
System.out.println("Thread:" + Thread.currentThread().getName() + " is run");
}
System.out.println(Thread.currentThread().getName()
+ " interrupt flag is " + isInterrupted());
} finally {
System.out.println("finally.............");
}
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread();
//设置线程为守护线程
endThread.setDaemon(true);
endThread.start();
//令主线程休眠5毫秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(5);
}
}
执行结果:
Thread:Thread-0 is run
Thread:Thread-0 is run
Thread:Thread-0 is run
Thread:Thread-0 is run
Thread:Thread-0 is run
Process finished with exit code 0
为什么finally不会被执行?
java线程分为两类,守护线程和非守护线程。当所有的非守护线程中止时,不论存不存在守护线程,虚拟机都会kill掉守护线程从而中止程序。 虚拟机中,执行main方法的线程就是一个非守护线程,垃圾回收则是另一个守护线程,main执行完,程序就中止了,而不管垃圾回收线程是否中止。 所以,如果守护线程中存在finally代码块,那么当所有的非守护线程中止时,守护线程被kill掉,其finally代码块是不会执行的。
synchronized分为:对象锁和类锁
对象锁:private synchronized void instance2(){};锁住的方法没用static修饰
类锁:private static synchronized void synClass(){};锁住的方法使用了static修饰
作用:在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronized既可以加在一段代码上,也可以加在方法上。
使用:synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
代码演示:
public class SynClzAndInst {
//使用类锁的线程
private static class SynClass extends Thread{
@Override
public void run() {
System.out.println("TestClass is running...");
synClass();
}
}
//使用对象锁的线程
private static class InstanceSyn implements Runnable{
private SynClzAndInst synClzAndInst;
public InstanceSyn(SynClzAndInst synClzAndInst) {
this.synClzAndInst = synClzAndInst;
}
@Override
public void run() {
System.out.println("TestInstance is running..."+synClzAndInst);
synClzAndInst.instance();
}
}
//使用对象锁的线程
private static class Instance2Syn implements Runnable{
private SynClzAndInst synClzAndInst;
public Instance2Syn(SynClzAndInst synClzAndInst) {
this.synClzAndInst = synClzAndInst;
}
@Override
public void run() {
System.out.println("TestInstance2 is running..."+synClzAndInst);
synClzAndInst.instance2();
}
}
//对象锁的方法
private synchronized void instance(){
SleepTools.second(3);
System.out.println("synInstance is going..."+this.toString());
SleepTools.second(3);
System.out.println("synInstance ended "+this.toString());
}
//对象锁的方法
private synchronized void instance2(){
SleepTools.second(3);
System.out.println("synInstance2 is going..."+this.toString());
SleepTools.second(3);
System.out.println("synInstance2 ended "+this.toString());
}
//类锁,实际是锁类的class对象
private static synchronized void synClass(){
SleepTools.second(1);
System.out.println("synClass going...");
SleepTools.second(1);
System.out.println("synClass end");
}
例1.使用对象锁同时锁住两个不同的对象synClzAndInst和synClzAndInst2
public static void main(String[] args) {
SynClzAndInst synClzAndInst = new SynClzAndInst();
Thread t1 = new Thread(new InstanceSyn(synClzAndInst));
SynClzAndInst synClzAndInst2 = new SynClzAndInst();
Thread t2 = new Thread(new Instance2Syn(synClzAndInst2));
t1.start();
t2.start();
//让主线程休眠1s
SleepTools.second(1);
}
使用对象锁锁住两个不同的对象,这两个线程t1和t2是并发执行的。
如果使用对象锁锁住同一个对象 很显然t1和t2线程是无法并发执行的。
执行结果:
synInstance is going...pers.amos.concurrent.syn.SynClzAndInst@16843df4
synInstance2 is going...pers.amos.concurrent.syn.SynClzAndInst@4bd8a307
synInstance ended pers.amos.concurrent.syn.SynClzAndInst@16843df4
synInstance2 ended pers.amos.concurrent.syn.SynClzAndInst@4bd8a307
例2.使用对象锁锁同一个对象synClzAndInst
public static void main(String[] args) {
SynClzAndInst synClzAndInst = new SynClzAndInst();
Thread t1 = new Thread(new InstanceSyn(synClzAndInst));
Thread t2 = new Thread(new Instance2Syn(synClzAndInst));
t1.start();
t2.start();
//让主线程休眠1s
SleepTools.second(1);
}
执行结果:
synInstance2 is going...pers.amos.concurrent.syn.SynClzAndInst@16843df4
synInstance2 ended pers.amos.concurrent.syn.SynClzAndInst@16843df4
synInstance is going...pers.amos.concurrent.syn.SynClzAndInst@16843df4
synInstance ended pers.amos.concurrent.syn.SynClzAndInst@16843df4
两个线程InstanceSyn和Instance2Syn分别会调用对象锁方法instance和instance2 如果锁住的是同一个对象,两个线程只能有一个线程获得锁 并且执行instance和instance2其中一个方法。
类锁 锁住的是每个类的class对象 该类的对象由JVM保证是唯一的。所以使用类锁时,只会有一个线程获得锁。
例3.使用类锁 锁住两个不同的对象 只能有一个线程获得锁并且执行
public static void main(String[] args) {
/* SynClzAndInst synClzAndInst = new SynClzAndInst();
Thread t1 = new Thread(new InstanceSyn(synClzAndInst));
Thread t2 = new Thread(new Instance2Syn(synClzAndInst));*/
SynClass synClass1 = new SynClass();
SynClass synClass2 = new SynClass();
synClass1.start();
synClass2.start();
//让主线程休眠1s
SleepTools.second(1);
}
执行结果:
TestClass is running...
TestClass is running...
synClass1 going...
synClass1 end
synClass2 going...
synClass2 end
总结:使用synchronized对 对象加锁时 如果是对象锁 应注意加到同一个实例对象上(如例2)而不是加到不同的对象上(如例1)
//在类中定义属性
private volatile int age = 100000;//初始100000
volatile关键字是线程不安全的 只能保证可见性不能保证age的原子性
当使用get方法获取age的值时,会强制从主存中读取age的值,无视当前线程中缓存的age的值
当使用set方法设置age的值时,会强制将该值刷新到主存中去。
public void setAge() {
age = age+20;
}
加法不是一个原子操作 JVM和操作系统会执行多条指令才能完成加法操作
用代码验证volatile关键字不能保证原子性:
public class VolatileUnsafe {
private static class VolatileVar implements Runnable{
private volatile int a = 0;
@Override
public void run() {
a = a + 1;
System.out.println(Thread.currentThread().getName() + "==============" + a);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
a = a + 1;
System.out.println(Thread.currentThread().getName() + "==============" + a);
}
}
public static void main(String[] args) {
VolatileVar volatileVar = new VolatileVar();
Thread t1 = new Thread(volatileVar);
Thread t2 = new Thread(volatileVar);
Thread t3 = new Thread(volatileVar);
Thread t4 = new Thread(volatileVar);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
代码执行结果:
Thread-0==============2
Thread-2==============3
Thread-1==============2
Thread-3==============4
Thread-3==============5
Thread-2==============6
Thread-1==============5
Thread-0==============7
理想结果应为从1-8顺序输出
总结:volatile的应用场景:适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。
线程变量。可以理解为是个map,类型 Map
保证每个线程只会使用自己那一份变量的拷贝 各个线程对同一个变量的修改不会相互影响
public class UseThreadLocal {
//可以理解为 一个map,类型 Map
static ThreadLocal<Integer> threadLaocl = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
/**
* 运行3个线程
*/
public void StartThreadArray(){
Thread[] runs = new Thread[3];
for(int i=0;i<runs.length;i++){
runs[i]=new Thread(new TestThread(i));
}
for(int i=0;i<runs.length;i++){
runs[i].start();
}
}
/**
*类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
*/
public static class TestThread implements Runnable{
int id;
public TestThread(int id){
this.id = id;
}
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
Integer s = threadLaocl.get();//获得变量的值
s = s+id;
threadLaocl.set(s);
System.out.println(Thread.currentThread().getName()+":"
+threadLaocl.get());
//threadLaocl.remove();
}
}
public static void main(String[] args){
UseThreadLocal test = new UseThreadLocal();
test.StartThreadArray();
}
}
代码执行结果:
Thread-0:start
Thread-1:start
Thread-2:start
Thread-1:2
Thread-0:1
Thread-2:3
一般ThreadLocal在线程池使用,每一个线程都相互隔离。保证了线程之间不会因共享的同一个变量而出现冲突。