计算机中的概念:
并发:进程(指两个或多个任务,在同一时间段内执行的(交替执行的))
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
并行:线程(指两个或多个任务,在同一时刻执行的(同时执行的))
线程是进程中的一个执行单元,负责当前进程中程序的执行,
一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
一个程序运行代表一个进程,一个进程可以有多个线程
线程的分类:
用户线程(普通线程):线程的代码必须执行完毕之后才能结束
守护线程(待在一个线程的后边):可以不用等到全部执行完毕就能结束线程,,,,陪着一个特定的线程,待特定线程死亡跟着死亡
主线程(顾名思义):就是执行主方法(main)的线程,该主线程是一个单线程的
线程的创建:
继承Thread类:创建一个类继承该类。进而重写run方法
实现Runnable接口:创建一个类实现该接口,进而重写run方法
public class MyThread extends Thread{@Overridepublic void run() {//待操作代码快//getName() 获取当前线程名称。返回值为String//Thread.currentThread.getName() 获取主线程名称 返回对当前正在执行的线程对象的引用}}
public class ThreadDemo {public static void main(String[] args) {MyThread mt = new MyThread(); //创建实例Thread th = new Thread(); //创建Thread类,也就是创建线程对象//Thread th = new Thread(mt); //创建一个带指定目标的线程对象//Thread th = new Thread(mt,"线程一"); //创建一个带指定目标和指定名称的线程对象//th.run() 此线程要执行的任务在此处定义代码(直接用start了,这个也就是个摆设)th.start(); // Java虚拟机调用此线程的run方法 -->进入就绪状态}
Thread&Runnable:
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势
1.适合多个相同的程序代码的线程去共享同一个资源。
2.可以避免java中的单继承的局限性。
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
其两者的作用大差不差,都能创建线程,后续的操作中使用的还是Thread中的方法
两者的区别在于一个是类一个是接口,点击Thread进去(public class Thread implements Runnable {})不难看出Thread也是实现了Runnable接口
所以在使用的过程中两者区别不大,复杂些的线程用Thread,简单的线程用Runnable
线程的生命周期:
线程的状态方法:
Sleep:就是执行到这句话的时候停一会,停多久?可以控制的,单位为毫秒(Thread.sleep(毫秒数);)
使线程停止运行一段时间,将处于阻塞状态
如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!
public class ThreadSleep extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}-------------------------------------------------------------------------------------------------public class ThreadSleepDemo {public static void main(String[] args) {ThreadSleep ts1 = new ThreadSleep();ThreadSleep ts2 = new ThreadSleep();ThreadSleep ts3 = new ThreadSleep();ts1.setName("曹操");ts2.setName("刘备");ts3.setName("孙权");ts1.start();ts2.start();ts3.start();}}
Join:就是让其他线程停下来,让去指定的线程走完,然后再回来走其他线程(线程名.join;指定先走的线程名哦,跟在指定线程的start后边)
阻塞指定线程,等到另一个线程完成以后再继续执行。
public class ThreadJoin extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}}-------------------------------------------------------------------------------------------------public class ThreadJoinDemo {public static void main(String[] args) {ThreadJoin tj1 = new ThreadJoin();ThreadJoin tj2 = new ThreadJoin();ThreadJoin tj3 = new ThreadJoin();tj1.setName("康熙");tj2.setName("四阿哥");tj3.setName("八阿哥");tj1.start();try {tj1.join();} catch (InterruptedException e) {e.printStackTrace();}tj2.start();tj3.start();}}
setDeamon:也就是设置拜把子兄弟,其中一个嗝屁了其他的也跟着嗝屁(线程名.setDeamon(true);)
可以将指定的线程设置成后台线程,守护线程;
创建用户线程的线程结束时,后台线程也随之消亡;
只能在线程启动之前把它设为后台线程
public class ThreadDaemon extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}}-------------------------------------------------------------------------------------------------public class ThreadDaemonDemo {public static void main(String[] args) {ThreadDaemon td1 = new ThreadDaemon();ThreadDaemon td2 = new ThreadDaemon();td1.setName("关羽");td2.setName("张飞");//设置主线程为刘备Thread.currentThread().setName("刘备");//设置守护线程td1.setDaemon(true);td2.setDaemon(true);td1.start();td2.start();for(int i=0; i<10; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}
setPriority:设置该线程去走程序更积极一些,并不是完全就是这个先走(线程名.setPriority(5);)
线程的优先级代表的是概率
范围从1到10,默认为5
public class ThreadPriority extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}}-----------------------------------------------------------------------------------------------public class ThreadPriorityDemo {public static void main(String[] args) {ThreadPriority tp1 = new ThreadPriority();ThreadPriority tp2 = new ThreadPriority();ThreadPriority tp3 = new ThreadPriority();tp1.setName("高铁");tp2.setName("飞机");tp3.setName("汽车");//public final int getPriority():返回此线程的优先级// System.out.println(tp1.getPriority()); //5// System.out.println(tp2.getPriority()); //5// System.out.println(tp3.getPriority()); //5//public final void setPriority(int newPriority):更改此线程的优先级// tp1.setPriority(10000); //IllegalArgumentException// System.out.println(Thread.MAX_PRIORITY); //10// System.out.println(Thread.MIN_PRIORITY); //1// System.out.println(Thread.NORM_PRIORITY); //5//设置正确的优先级tp1.setPriority(5);tp2.setPriority(10);tp3.setPriority(1);tp1.start();tp2.start();tp3.start();}}
yield:也就是让行,我不先走这个程序的全部,我运行一部分然后让其他线程运行,然后我再运行
让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态;
调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行!
public class ThreadYield extends Thread{@Overridepublic void run() {System.out.println(getName()+"-->start");Thread.yield(); //礼让System.out.println(getName()+"-->end");}}-------------------------------------------------------------------------------------------------public class YieldDemo {public static void main(String[] args) {ThreadYield ty1 =new ThreadYield();ThreadYield ty2 =new ThreadYield();ty1.setName("a");ty2.setName("b");ty1.start();ty2.start();}}
线程同步:
线程安全:当多个线程操作同一份数据时,数据的结果有可能跟我们想像的不一样,这就是线程安全问题
同步代码块:关键字 ( synchronized )
synchronized(同步锁){ //对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
需要同步操作的代码
}
锁对象 可以是任意类型。<br />多个线程对象 要使用同一把锁。
public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private int ticket = 100;//创建一个锁对象Object obj = new Object();//设置线程任务:卖票@Overridepublic void run() {//使用死循环,让卖票操作重复执行while(true){//同步代码块synchronized (obj){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}}}----------------------------------------------------------------------public class Demo01Ticket {public static void main(String[] args) {//创建Runnable接口的实现类对象RunnableImpl run = new RunnableImpl();//创建Thread类对象,构造方法中传递Runnable接口的实现类对象Thread t0 = new Thread(run,"窗口1");Thread t1 = new Thread(run,"窗口2");Thread t2 = new Thread(run,"窗口3");//调用start方法开启多线程t0.start();t1.start();t2.start();}}
同步方法:
public synchronized void method(锁){
可能会产生线程安全问题的代码
}
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
锁:
锁的进阶==》先考虑对象锁,其次是this锁,如果前两个仍不能保证数据的完整性,考虑使用字节码对象锁(类名.class)
public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private static int ticket = 100;//设置线程任务:卖票@Overridepublic void run() {System.out.println("this:"+this);//this:com.itfxp.Synchronized.RunnableImpl@58ceff1//使用死循环,让卖票操作重复执行while(true){payTicketStatic();}}/*静态的同步方法锁对象是谁?不能是thisthis是创建对象之后产生的,静态方法优先于对象静态方法的锁对象是本类的class属性-->class文件对象(反射)*/public static /*synchronized*/ void payTicketStatic(){synchronized (RunnableImpl.class){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}/*定义一个同步方法同步方法也会把方法内部的代码锁住只让一个线程执行同步方法的锁对象是谁?就是实现类对象 new RunnableImpl()也是就是this*/public /*synchronized*/ void payTicket(){synchronized (this){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}}
Lock锁:同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
package com.itheima.demo09.Lock;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*卖票案例出现了线程安全问题卖出了不存在的票和重复的票解决线程安全问题的三种方案:使用Lock锁java.util.concurrent.locks.Lock接口Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。Lock接口中的方法:void lock()获取锁。void unlock() 释放锁。java.util.concurrent.locks.ReentrantLock implements Lock接口使用步骤:1.在成员位置创建一个ReentrantLock对象2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁*/public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private int ticket = 100;//1.在成员位置创建一个ReentrantLock对象Lock l = new ReentrantLock();//设置线程任务:卖票@Overridepublic void run() {//使用死循环,让卖票操作重复执行while(true){//2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁l.lock();//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;} catch (InterruptedException e) {e.printStackTrace();}finally {//3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁l.unlock();//无论程序是否异常,都会把锁释放}}}}}
