程序
想为完成任务,用指定的语言编写的一组指令的集合
简单的讲就是我们写的代码
进程
进程指的是运行中的程序
比如我们使用qq,就启动了一个进程
系统会为该进程分配内存空间
进程是程序一次执行的过程,或者正在运行的一个程序。是动态过程:有他自身的产出存在消亡的过程
线程

迅雷同时下载多个文件
窗口是一个进程
一个文件下载就是一个线程
单线程和多线程

并发和并行

并行
只是来回切换程序,并不是真正的同时进行
比如你打电话开车
只是来回切换状态,只是切换太快,没有感觉
而
并发
是同一个时刻多个程序同时执行
一个cpu运行一个程序
如果说并发总也出现了
一个cpu执行多个任务
你就是并发中的并行喽


package com.hansp.thread_;
public class Demo01 {
public static void main(String[] args) {
A a = new A();
//a.start();这里不能调用start,因为没有该方法
Thread thread = new Thread(a);
thread.start();//创建一个thread然后调用他的start要把我们实现接口的对象当做形参
}
}
class A implements Runnable{//通过实现Runnable接口,开发线程
int count=0;
@Override
public void run() {
while (true)
{
System.out.println("小狗汪汪叫。。hi"+(++count)+"线程名称"+Thread.currentThread().getName());
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(count==10)
break;
}
}
}

这里需要用一个构造器
传入实现接口对象
取得Thread对象
最后才可以调用
start()创建线程
至于这个构造器的细节看底层那里
下面看代码
package com.hansp.thread_;
public class CPUNumber {
public static void main(String[] args) {
cat cat = new cat();
cat.start();//启动线程,会自动调用run
//具体看的曾分析吧
}
}
//1.当一个类继承了Thread类,该类就可以当做线程使用
//2.重写run方法,协商自己的业务代码
//3.run Thread类实现了Runnable接口的run方法
/*Thread里面的run的源码
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class cat extends Thread{
@Override
public void run() {
int times=0;
while (true) {
//该线程每隔一秒。在控制台输出“喵喵,我是小猫咪”
System.out.println("喵喵,我是小猫咪"+(++times));
//让该线程休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(times==80){//当输出80次时退出
break;
}
}
}
}



建议多用Runnable接口来创建线程
进程到线程
线程全部执行完毕后进程才会完毕
多线程编程中,不是主线程结束了,进程就结束了,需要所有的线程都运行完成进程才会结束
System.out.println("喵喵,我是小猫咪"+(++times)+"线程名"+Thread.currentThread().getName());
Thread.currentThread().getName()
这行代码可以获取到所在位置的线程的线程名

可以发现cat这个类启动的线程名叫Thread-0
但是此时main线程还是继续执行的

不会说卡在start方法
等着run的语句执行完,他们两个是一起执行的
在执行的效果可以看出
主线程和子线程交替执行
相当于在mian线程里面开了个Thread-0线程
用一个图来表示

但是主线程执行完,也不会停止其子线程运行完
只有运行完然后子线程会退出
多线程编程中,不是主线程结束了,进程就结束了,需要所有的线程都运行完成进程才会结束
然后进程就无了
如果把
cat.start()
改为
cat.run()
相当于是没有开辟线程的
就是普通的创建对象->调用方法
要把run方法执行完后才执行主程序的代码
那就不是多线程了,这叫串行化
而且线程名还是会输出main,没有开辟Thread-0
start源码+解读
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
start实现多线程的关键方法start0()
start0()是一个本地(native)方法底层是由JVM调用的
我们不能调用
一些小细节

这里用了一个设计模式(代理模式)
这里用代码来模拟
Runnable接口开发线程的机制
//线程代理类,模拟了一个极简的Thread类
class Proxy implements Runnable{//你可以把Proxy(代理)类当做Thread类对待
private Runnable target = null;
@Override
public void run() {
if (target!=null){
target.run();
}
}
public Proxy (Runnable target){//构造器
this.target=target;
}
public void start(){
start0();
}
public void start0(){
run();
}
}
差不多就是把我们的
Runnable接口实现的对象赋给那个Thread的一个Target(也是Runnable类型)
然后调用Thread的的start的话
会调用Thread的start0()
新建线程
新建完线程以后
start0()调用target.run(),然后动态绑定到最开始那个实现Runnable的类型的run
相当于只用了
我们传进来的那个类型的run方法
其余都是Thread类操作还是调用的Thread的start()和start0()方法
这叫代理模式
package com.hansp.thread_;
@SuppressWarnings({"all"})
public class Demo02 {
public static void main(String[] args) {
M m = new M();
M m1 = new M();
Thread thread = new Thread(m);
Thread thread1 = new Thread(m1);
thread.start();//第一个线程
thread1.start();//第二个线程
}
}
class M implements Runnable{
@Override
public void run() {
int count=0;
while (true){
System.out.println("hi"+(++count)+"线程名"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count==10){
break;
}
}
}
}

相当于一个main线程开了两个线程
一个Thread-0
一个Thread-1
图像理解

package com.hansp.thread_;
@SuppressWarnings({"all"})
public class Demo03 {
public static void main(String[] args) {
C c = new C();
C c1 = new C();
C c2 = new C();
c.start();
c1.start();
c2.start();
}
}
@SuppressWarnings({"all"})
class C extends Thread {
private static int times=100;
@Override
public void run() {
while (true){
if(times<=0){
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
}
}

这种方式会出现线程不安全的弊端
什么导致这种情况的发生呢
分析
线程经过判断,然后对数据进行操作

当times==1时
可能第一个线程使time–还没有发生(中间也有sleep)
第二个线程判断就已经完了,相当于,判断没有生效
导致time又被减1
同理第三个线程也可能导致,在前两个没执行完也判断完,也使times–
最后1-3=-2
package com.hansp.thread_;
@SuppressWarnings({"all"})
public class Demo03 {
public static void main(String[] args) {
C c = new C();
C c1 = new C();
C c2 = new C();
Thread thread1 = new Thread(c);
Thread thread2 = new Thread(c1);
Thread thread3 = new Thread(c2);
thread1.start();
thread2.start();
thread3.start();
}
}
@SuppressWarnings({"all"})
class C implements Runnable {
private static int times=100;
@Override
public void run() {
while (true){
if(times<=0){
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
}
}

也是会出现之前那种情况
超卖和一票多卖
都是有可能的
具体结局办法在下面的Synchronzied

1.正常线程运行完后就会自动终止
2.主线程通过子线程的控制run方法来实现
终止子线程的运行
package com.hansp.thread_;
public class exit_ {
public static void main(String[] args) {
T t = new T();
t.start();
//如果希望主线程控制t线程的终止,必须可以修改loop变量
//让t退出run方法从而终止t线程
System.out.println("主线程休眠十秒钟");
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t.setLoop(false);
}
}
@SuppressWarnings({"all"})
class T extends Thread{
private int count=0;
private boolean loop=true;
@Override
public void run() {
while (loop){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("运行中");
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}

1.setName(String name)
2.getName(Thread a)
3.开辟线程
4.普通调用对应线程类的run方法
5和6放下面单独讲
7.sleep(毫秒数)使调用的线程休眠对应的毫秒数
8.注意,中断线程不是停止线程,此方法一般用于中断正在休眠线程
try {
Thread.sleep(100);
} catch (InterruptedException e) {//InterruptedException e就是一个中断异常
//当中断正在休眠的线程就会执行下面的代码
throw new RuntimeException(e);
}


1.线程.yield()
package com.hansp.thread_;
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
Q q = new Q();
Thread thread = new Thread(q);
thread.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi");
}
thread.join();//也可以在循环中用if(i=5)join
//如果想达到5时候插入
//可以
//if(i==5){
//thread.start();thread.join();
//}
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi");
}
}
}
class Q implements Runnable{
int i=0;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hi"+(++i));
if (i==10){
break;
}
}
}
}
输出
hi
hi1
hi
hi2
hi
hi3
hi
hi4
hi5
hi
hi6
hi7
hi8
hi9
hi10
hi
hi
hi
hi
hi
比如说两个线程一起执行
t1和t2
在t1线程里调用t2.join()
会先执行t2
然后指向t1
注意只有你在线程a调用b.join()
只有线程a会等你执行完再执行如果线程a里面还有线程c
c会和b一起执行
如图

package com.hansp.thread_;
@SuppressWarnings({"all"})
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
C c = new C();
Thread thread = new Thread(c);
thread.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(10);
System.out.println("hi"+i);
}
thread.interrupt();
}
}
@SuppressWarnings({"all"})
class C implements Runnable {
private static int times=100;
@Override
public void run() {
while (true){
if(times<=0){
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
System.out.println("被中断了" );
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
}
}

thread.interrupt();
运行到
中断语句时会中断thread线程的睡眠,然后运行catch的语句
如果catch语句没有导致程序停止运行
然后继续执行
就是打断线程休眠,让他提前干活
线程的优先级:
1.MAX_PRIORITY :10
MIN_PRIORITY :1
NORM_PRIORITY :5 -->默认优先级
2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(Thread p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程的cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

这么说
main线程启动一个thread-0线程
如果mian线程结束的话
thread-0也随之结束
这个thread-0线程就叫做main线程的守护线程
下面展示如何吧一个线程设置成守护线程
正常情况
主线程运行完后
子线程还在跑
package com.hansp.thread_;
import java.lang.Thread;
@SuppressWarnings({"all"})
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
C c = new C();
Thread thread = new Thread(c);//设置成守护线程之需要在这加thread.setDaemon(true);一定要在start()前
thread.start();
for (int i = 0; i < 10; i++) {//main线程
System.out.println("工作");
Thread.sleep(1000);
}
}
}
@SuppressWarnings({"all"})
class C implements Runnable {
private static int times=0;
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("被中断了" );
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出第"+(++times)+"票");
}
}
}
如果我们想主线程结束后thread-0线程也退出
需改为守护线程
只需要加一句
thread.setDaemon(true);
注意这句话要写在thread.start()前面
官方的六种状态,至于为什么说7种,看下面的流程图

线程生命周期的转换图

所说的7大状态实则
就是把Runnable状态细分为Running和Ready状态
一种是准备好运行不过还没运行
一种是正在运行
Runnable只是代表可以运行
接下来一个个讲
1.Runnable
运行状态
内分为Ready和Running状态
准备运行和正在运行状态
2.Terminated
终止状态
当一个线程运行完后就会进入终止状态
3.Blocked
就是等着拿到锁
然后线程进入对应代码
执行
4.Waiting
当这个线程比如说调用
别的线程join
或者用wait方法
会进入等待状态
5.TimedWaiting
超时等待状态
sleep
或者join也可能使线程进入该状态
6.NEW状态
只有new一个线程的时候
才会进入这个状态
可以通过 线程.getState() 查看对应线程状态

第六行代码
只要
t线程不没有结束
就一直循环


一个数据被多个线程访问
可能会导致线程安全问题的发生
如上面的例子
我们用关键字
Synchronized
可以防止多个线程同时操控同一个数据
就可以等一个线程操作完数据后
再让另一个线程进入操作该数据(内存)

1.代码块内的代码
同一时间内只有一个线程能够操作
传入的对象有对应的对象锁
只有一个线程得到对应的对象锁
才能进入该同步代码块
一个线程操作完,返回对象锁
然后下一个线程得到对象锁,再来运行同步代码块的运行
2.同步方法
不管有多少个线程
同一时间只有一个线程能操作
m(同步)方法
3.是1和2生动的比喻
改编售票代码
package com.hansp.thread_;
@SuppressWarnings({"all"})
public class Demo03 {
public static void main(String[] args) {
C c = new C();
Thread thread = new Thread(c);
Thread thread01 = new Thread(c);
Thread thread02 = new Thread(c);
thread.start();
thread01.start();
thread02.start();
}
}
@SuppressWarnings({"all"})
class C implements Runnable {
private static int times=100;
private boolean loop=true;
public synchronized void sell(){//同步方法在同一时刻只能有一个线程执行sell方法
if(times<=0){
System.out.println("售票结束...");
loop=false;
return;
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
@Override
public void run() {
while (loop){
sell();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

这样就不会超卖了
注意
1.
if(times<=0){
System.out.println("售票结束...");
loop=false;
return;
}
这里一定要加return;
因为只有方法是同步锁
如果线程1进入
正好卖完times==0
此时loop=false
但是卡线程2和线程3的地方不是循环
是方法
线程1输出完后
线程2已经在等着进入了,不会在while(loop)判断等很久
所以一定要return
防止times已经变为0
但是还是会执行
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
这条语句导致超卖
2.sleep最好放外面
可能有只有一个线程在卖票
然后卖完的情况
把sleep从同步方法移到不同步的
run方法
或者把票数改大一点
就能看见多个窗口卖票的情况了

t1,t2和t3三个线程抢锁
谁抢到谁先进去操作相关代码
比如t1先进去

操作完后t1返回
把锁还回去然后继续和t2t3争抢
当然可能还是t1又抢夺到了
因为Synchronized这是个非公平锁,谁抢到就是谁的

关于5和6
5.是加在同一个对象上的
必须是同一个线程对象开启的线程互相之间才会收到锁的影响
不同对象的话不会
同步方法比如上面的售票改版
就是把锁加到this上了,即调用该方法的对象
如果想把锁加到别的对象上,可以用同步代码块
public synchronized void sell(){//同步方法在同一时刻只能有一个线程执行sell方法
if(times<=0){
System.out.println("售票结束...");
loop=false;
return;
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
改为
public void sell(){//同步方法在同一时刻只能有一个线程执行sell方法
synchronized(this) {if(times<=0){
System.out.println("售票结束...");
loop=false;
return;
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"
+"剩余"+(--times)+"票");
}
}
两种效果是相同的
都是把互斥锁加到this对象上
当然可以换成别的对象(不过要保证对象是同一个)
比如Object a=new Object();
synchronized(a){
}
也可以
因为,都是同一个线程对象开辟的线程
使用这个对象的a肯定都是同一个(同一个object)
也一static new一个对象
这样的话就算是不同的对象a只要是该类的对象
那么这个对象锁也是同一个
6.是只要是这个类创建的对象都可以受到对应锁的影响
对应的static方法里用
synchronized代码块
不能这样写

需要这样写
就是
类名.class




首先
run方法不是同步方法
可以好几个线程一起进入
里面有两个同步代码块
第一个同步代码块需要先获得o1然后获得o2才可以完成代码块执行
但是比如说线程1进入第一个代码块拿到了o1
此时,可能flag恰好改变
线程2也进入到了第二个代码块拿到了o2
这时线程1拿不到o2
线程2拿不到o1
形参了线程死锁的状态


这就是典型的线程死锁现象
后面的true和false是赋值给flag的
注意:o1和o2都是static的,是属于类的,所以A和B两个不同的对象用的却是同一把锁

