/**
* 程序 :
* 就是指我们写的保存硬盘当中的静态文件,叫程序
* 进程 :
* 就是程序被加载进内存,准备或者正在运行时,就叫进程
* 线程 :
* 就是程序中不同的执行路径
*
* 线程 :
* 当我们程序开始执行的时候,就是java中main()方法开始执行,在栈内存中开辟main()栈帧的时候,就是开启了一个线程
* 一个线程就是一个栈及其里面的链式栈帧,一个栈帧就对应一个方法.
*
*/
☆☆☆☆ 重点 ☆☆☆☆ !
创建一个新的线程有两种方式,
但是启动一个线程,只有一种方式(唯一的一种);
//三个要点 :
线程的定义;
线程的启动;
线程开启之后;
// 线程的定义 :
程序不同的执行路径,从代码角度来讲,线程就是一个类,从内存角度来讲,线程就是一个栈和链式栈帧
// 线程必须要先启动 :
start();//成员方法
// 一旦开启多线程 :
这个时候,说代码是从左到右,从上往下执行这句话就不准确了,因为,我们开启了两个线程,在每个线程的内部,仍然是从上到下,从左到右,但是线程之间的代码,没有固定的先后顺序,他们执行的先后顺序,是不确定的,和CPU,操作系统,和我们自己的设定,都相关
Thread :
//创建线程
创建一个新的线程,有两个方式 :
//第一种 :
第1步 : 继承java.lang.Thread;这个类
第2步 : 重写run()方法,为什么要重写?因为run()就相当于新线程的main()方法,就是程序执行的起点和终点;
//创建了线程,不启动,不能用 : start();
//开启线程 :
Thread t = new 子类名();
t.start();//就开启线程了
Runnable :
//第二种 :
第1步 : 实现java.lang.Reunnable接口
第2步 : 实现接口的run()方法;
//开启线程 :
1 Thread t = new Thread(new 子类名());
t.start();//开启线程
2 Runnable(或者是子类名) r = new 子类名();//多态
Thread t = new Thread(r);
t.start();//开启线程
Thread_start :
start();//启动线程(唯一的方法) 默认会去调用该线程的run()方法 成员方法
启动一个线程 : 只能是Thread类中的start()方法;
currentThread :
currentThread();//获取当前线程的内存地址 静态方法,类名调用就行 对象名也行,底层会默认转换为类名
Thread.currentThread();//获取当前线程的内存地址(在哪个线程写的,Thread就是哪个线程)
setName :
setName();//给线程取名字,不设置默认是Thread-0,依次累加
Thread t1 = new 子类名();//默认的名字是在new的时候取得 Thread-0
t1.setName("t1");//现在,线程名字就改为t1了
t1.start();
getName :
getName();//获得当前线程的名字 成员方法
Thread.currentThread().getName();//因为getName()是成员方法,所以想调用这个方法,要先获得当前线程的内存地址吧,就是Thread.currentThread();
sleep :
sleep : 在那个线程写的,就锁定那个线程,和 用哪个线程的引用调用sleep()没有关系(因为底层也会默认转成类名)
//让某个线程休眠 可以直接类名调用就行了
//线程中,加了sleep(),就必须抛异常或者捕捉异常
sleep();//让当前线程睡眠指定的毫秒数 是静态方法 类名调用就行
Thread.sleep(1000);//让该线程睡眠 1 秒;写在那个线程,就睡眠那个线程
setPriority :
setPriority();//设置线程的优先级 成员方法
线程的默认优先级 :
//子线程默认继承父类的线程优先级
java中线程的优先级有0-10 11个级别
win当中是1-7 7个优先级
MIN_PRIORITY : 1 //最低
MAX_PRIORITY : 10 //最大
NORM_PRIORITY : 5 //正常
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);//(设置当前线程的优先级)成员方法要用对象引用去调用吧.先获取当前线程的内存地址,调用setPriority();去设置优先级 这里设置的为 1 最低的 不设置默认为 5 NORM_PRIORITY
Thread t = new 子类名();
t.setPriority(Thread.MAX_PRIORITY);//这和上面那个是一样的,只不过上面那个,写在哪,就设置那个线程的优先级,这个是设置指定的那个线程的优先级
//唤醒休眠的两种方法 :
1 Interrup();//异常唤醒,需要捕捉和抛异常
2 while();//循环
Interrup :
Interrup();//这种方式唤醒,一定要try...catch..处理异常或者是throws抛异常
//异常类型 : InterruptedException
Thread t = new 子类名();//这种是继承Thread并覆写run()方法的,这种创建线程方式
Thread t = new Thread(new 子类名());//这种是实现Runnable接口,并实现run()方法的,这种创建线程方式
t.start();//启动线程
t.interrupt();//强行唤醒(异常唤醒)
while :
用while循环(或者别的循环,加if),
需要强行唤醒,就在主线线程设置 引用(对象名).run=false;就行了
boolean run = true;//成员变量
int i =0;//成员变量
public void run(){//线程类的run()方法
while(run){//循环 这里如果为true 就会一直循环(死循环把)
i++;
MIN_PRIORITY : 1
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName()+"--->"+i );
}
}
join :
join();//也就是线程的合并,把两个线程,合并成一个线程(成了单线程) 成员方法
等着当前线程执行完毕,然后再在本线程后面继续往下执行,当前行以后,所有的main线程的代码,都必须等着这个线程执行完毕以后再执行,相当于这个线程和main线程合并,变为单线程程序,又是从上往下执行
Thread t = new 子类名();
t.start();//启动线程
t.join();//这行一下的main线程的代码,都必须等待t执行完,才能继续执行
yield :
yield();//静态方法 可以直接类名调用,让当前线程让位一次(只跟同级)
1 这是一个静态方法,一维这:即使用对象调用他,也会在底层转换为类名调用,停掉的也还是当前所在的线程,也就是说在那个类中调用,就停止那个线程
2 给同一个优先级的线程让位,不同的优先级,不让位
3 和sleep()方法相同,那个线程调用就停止那和累的线程,但是没有时间,不固定,只是让出当前这个时间片,下一个时间片让不让,另说
Thread.yield();//跟同级线程,让位一次 写在哪,哪个就让位一次
synchronized: :
synchronized : 并不是往对象里面加锁,锁是每个对象都有的属性,synchronized只是锁上了这把锁,是个持续的动作而已
synchronized 是一个修饰符,修饰的方法,等于是给方法加了一把钥匙,当一个线程执行这个方法的时候,别的所有线程都不能再去执行这个方法,和用synchronized修饰的方法,一个方法锁住,所有用synchronized修饰的方法都会锁上,但是没有用synchronized修饰的方法,可以访问//目的就是让数据同步
//线程同步本质 :
当多个线程(服务,任务),操作同一个数据的时候,保证数据一致,本质就是数据同步,是一种数据安全机制
这种机制,操作上更多是从数据上来保护的
//异步编程模型 :
线程是完全独立的,谁的运行也不会受到别的线程的影响
//同步编程模型 :
线程间不是独立的,互相间有影响的,某些线程必须单独完成任务以后,才能 够让别的线程执行(为什么?)
//同步的原因 :
1 同步是指数据的同步,为了数据安全,必须要等某个线程对数据操作完成以后,在引入别的线程进行操作
同步机制,某种程度是多线程编程变成了单线程
2 同步的环境
1 必须多线程
2 多个线程有同时操作同一个数据的可能性
3 主要就是涉及数据更改的操作
线程同步是数据的概念
//方法 : 锁定,锁定是一个逻辑上的概念,是为了保证同步的手段
//方发锁 : 按照封装性,根本不可能再是直接就能对数据进行操作只能通过方法进行操作
//对象锁 : 堆内存,只要一加锁,谁也进不来
//类锁 : 更大级别的
只要加了 synchronized 修饰的成员方法(静态方法),是多个线程不可以用加了synchronized的方法同时访问这个对象(类)
同一个方法互斥,不同的方法也互斥
//静态方法声明 :
又称为类锁;
public synchronized static void m1(){
System.out.println( "不管谁被锁,我都会被锁,他解开,我才解开" );
}
public synchronized static void m2(){
System.out.println( "不管谁被锁,我都会被锁,它解开,我才解开" );
}
public static void m3(){
System.out.println( "别人锁不锁,干我何事" );
}
比如我有两个线程,t1和t2把
t1调用了m1();/*就会锁住*/ t2就不能调用m1()和m2();/*m2()也会被锁住*/只能调用m3();//因为m3()没有用synchronized修饰
//成员方法声明 :
又称对象锁
前提是/*对象*/必须是同一个对象
public synchronized void m1(){
System.out.println( "不管谁被锁,我都会被锁,他解开,我才解开" );
}
public synchronized void m2(){
System.out.println( "不管谁被锁,我都会被锁,它解开,我才解开" );
}
public void m3(){
System.out.println( "别人锁不锁,干我何事" );
}
比如我有两个线程,t1和t2把//并且t1线程和t2线程都是同一个对象
t1调用了m1();/*就会锁住*/ t2就不能调用m1()和m2();/*m2()也会被锁住*/只能调用m3();//因为m3()没有用synchronized修饰
setDaemon :
setDaemon();//设置守护线程(兜底线程)
只要主线程执行完毕,不管守护线程执行了多少,执行到哪,都不会在执行,也跟着死去,但是跟主线程晚结束一下,
//活,他先活,死,他先死(要么怎么守护,守护线程,先活,后死)
Thread t1 = new 子类名();//创建一个线程把,用的是继承的方式
t1.setDaemon(true);//里面是布尔型 true/false 把当前线程设置为守护线程
t1.start();//启动t1线程把
//如果我守护线程里面有个死循环(就是不会结束的那种),但是,只要主线程执行完毕,守护线程也会跟着结束
Timer :
1 创建一个定时器(Timer)
2 创建一个执行定时器任务的类,这个类必须继承于 TimerTask 这个类,并且,必须覆写 TimerTask 类中的 run(),方法.(TimerTask)
3 用 Timer 类里面的 schedule()方法,分配并执行定时器任务(schedule)
Timer :
Timer t = new Timer();//创建一个定时器,并且调用Timer类里面的 schedule()方法,分配执行的任务
TimerTask :
class 类名 extends TimerTask{
public void run(){
System.out.println( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) );//获取当前时间,输出
}
}
Date :
Date d = new Date();//获取当前的时间
String d1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(d);//以这种形式,输出当前时间d
schedule :
schedule();//Timer里面的schedule(要执行任务的类对象 , 起始的时间 , 任务执行间隔的毫秒数 );//循环执行方法,默认调用run()方法
t.schedule(
new 类名(),/*执行定时器任务的类的对象*/
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2015-09-10 14:48:00 000"),/*其实时间(从什么时间,开始执行)*/
1000*3)/*执行任务的间隔时间*/;
synchronized :
//死锁
synchronized : 并不是往对象里面加锁,锁是每个对象都有的属性,synchronized只是锁上了这把锁,是个持续的动作而已
死锁 : 死锁就是大家都访问不了的状态;
原理 :
1 两个线程,两个对象
2 先后 嵌套 锁定 执行 两个对象(先锁定第一个对象,再嵌套锁定第二个对象)
3 先后 嵌套 锁定 执行 两个对象(先锁定第二个对象,再嵌套锁定第一个对象)
4 在第一个线程执行过程中,执行到第二个对象的时候,发现第二个被锁死了,也只能等待
5 第二个线程执行过程中,执行到第一个对象的时候,发现第一个被锁死了,也只能等待
public class Thread线程{
public static void main(String[] args){
//这里我用Object是因为不用再写实体类了,这个类可以自己写
Object o1 = new Object();//第一个对象
Object o2 = new Object();//第二个对象
Thread t1 = new Thread(new T1(o1,o2));//第一个线程(调用的是有参构造把)
Thread t2 = new Thread(new T2(o1,o2));//第二个线程
t1.start();//启动线程
t2.start();//启动线程
}
}
class T1 implements Runnable{//线程类吧
Object o1;//成员变量 这里用于接收实参传进来的Object的对象引用(实体类用的那个类,这里就要用那个类去接吧)
Object o2;
T1(Object o1,Object o2){//有参构造
this.o1 = o1;//赋值
this.o2 = o2;
}
public void run(){//覆写的run()方法
synchronized(o1){//锁定第一个对象 (o1)
try{
Thread.sleep(1000);//不睡眠一秒,有时候不死锁,睡眠一秒,百分百死锁(因为如果是我t1线程执行完了,t2线程在开始执行的呢?那就不是死锁了吧,这样,t1到这停一秒,一秒足够t2也走的o2的那个对象那里,所以就死锁了,都在等待)
}catch(Exception e){
e.printStackTrace();
}
synchronized(o2){//在锁定第一个对象(o1)中 嵌套锁定第二个对象(o2)
System.out.println( "t1" );//如果,t1执行完成了,就会输出 但是如果死锁了,t1就进不来吧 就不会输出
}
}
}
}
class T2 implements Runnable{//创建线程类
Object o1;//成员变量 这里用于接收实参传进来的Object的对象引用(实体类用的那个类,这里就要用那个类去接吧)
Object o2;
T2(Object o1, Object o2){//和上面一样,不懂就看上面的注释
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized(o2){//t1线程先锁定的o1,我这里如果先锁定o2,没有关系吧,t1能进o1,但是我也能进o2 ,因为是不同的对象,但是我如果再想进o1,是不是就进不去了(因为t1进去就锁了)
try{
Thread.sleep(1000);//这个也停一秒,就是当t2进来o2这个对象,并锁定了,t1也肯定就能进去o1这个对象,并也锁定了,现在就开始进入等待了吧,就是死锁
}catch(Exception e){
e.printStackTrace();
}
synchronized(o1){//嵌套锁定o1
System.out.println( "t2" );
}
}
}
}
wait :
wait是 Object 类的方法 ,不是Thread中的方法 Thread中wait也是继承于 Object
注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理
this.wait();不是让当前对象wait,而是让当前锁定this对象的线程wait,同时 释放对this的锁定
//wait()和sleep()的区别 :
wait : wait()会把锁打开,让别的线程进来,并且自身进入睡眠,只能等待唤醒,如果不唤醒,会一直休眠(并且,当前线程wait()下面的代码不会执行,只能等待唤醒,才能执行)
sleep : sleep()就算设置了休眠,但是锁也不会打开,别的线程也进不来,并且可以唤醒,但是如果不唤醒,到了指定的睡眠时间,自身也会唤醒(并且,当前线程sleep()下面的代码不会执行,只能等待唤醒,才能执行)
this.wait();//如果这个休眠,会打开锁,让别的线程执行
System.out.println( "我在睡眠" );//不会执行,如果没有线程唤醒,永远不会执行,会永远睡眠
Thread.sleep(5000);//睡眠5秒,并且不会打开锁,别的线程也得等着,一样进不来
System.out.println( "我快唤醒了" );//如果没有线程唤醒当前睡眠的线程,那么,5秒后,自动醒,一样会输出
notify :
notify是Object 类的方法 ,不是Thread中的方法 Thread中notify也是继承自Object
注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理
notify() : 随机唤醒一个在该对象上睡眠的某一个线程(一般都是谁先睡眠,谁先醒,具体不知道,是系统随机唤醒的),并且,没有让自身进入睡眠状态
this.notify();//随机唤醒一个在该对象上睡眠的线程
System.out.println( "我能输出" );//我能输出,因为上面只是唤醒了一个线程,并没有让自身进入睡眠状态
notifyAll :
notifyAll是Object 类的方法 ,不是Thread中的方法 Thread中notifyAll也是继承自Object
注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理
notifyAll() : 唤醒在该对象上,所有等待的线程,并且,没有让自身进入睡眠状态
this.notifyAll();//唤醒所有在该对象上睡眠的线程
System.out.println( "我能输出" );//我能输出,因为上面只是唤醒了所有线程,并没有让自身进入睡眠状态