计算机中的概念:
并发:进程(指两个或多个任务,在同一时间段内执行的(交替执行的))
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
并行:线程(指两个或多个任务,在同一时刻执行的(同时执行的))
线程是进程中的一个执行单元,负责当前进程中程序的执行,
一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
一个程序运行代表一个进程,一个进程可以有多个线程
线程的分类:
用户线程(普通线程):线程的代码必须执行完毕之后才能结束
守护线程(待在一个线程的后边):可以不用等到全部执行完毕就能结束线程,,,,陪着一个特定的线程,待特定线程死亡跟着死亡
主线程(顾名思义):就是执行主方法(main)的线程,该主线程是一个单线程的
线程的创建:
继承Thread类:创建一个类继承该类。进而重写run方法
实现Runnable接口:创建一个类实现该接口,进而重写run方法
public class MyThread extends Thread{
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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{
@Override
public 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();
//设置线程任务:卖票
@Override
public 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;
//设置线程任务:卖票
@Override
public void run() {
System.out.println("this:"+this);//this:com.itfxp.Synchronized.RunnableImpl@58ceff1
//使用死循环,让卖票操作重复执行
while(true){
payTicketStatic();
}
}
/*
静态的同步方法
锁对象是谁?
不能是this
this是创建对象之后产生的,静态方法优先于对象
静态方法的锁对象是本类的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();
//设置线程任务:卖票
@Override
public 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();//无论程序是否异常,都会把锁释放
}
}
}
}
}