目录
一、线程
1、进程
* 进程:是处于运行过程中的程序、具有独立的功能、是系统进行资源分配和调度的独立单位
01.进程特点
1、独立性:进程是系统中独立存在的实体,他们拥有自己独立的资源,每个进程都拥有自己私有的抵制空间,在没有经过进程本身允许下、一个进程不能直接访问其他进程的地址空间 2、动态性:进程是一个正在系统中活动的指令集合,包含了时间的概念,具有自己的生命周期状态; 3、并发性:在单个处理器上,多线程可以并发执行,并且在执行时他们彼此之间不会互相的影响。
02.并发和并行区别
并行 : 在同一时刻,有多条指令在多个处理器上同时执行 并发 : 1、在同一时刻,某个处理器只能执行一条指令。 2、多个进程的指令可以被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果。
2、线程
1、线程扩展进程的概念,使得同一个进程可以同时并发处理多个任务 2、线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程 3、线程不拥有系统资源,他与父线程中的其他线程共享父进程所拥有的全部系统资源
3、线程优点
1、容易共享内存: 进程之间不能够共享内存,但线程之间可以共享内存,因为与分隔的进程相比,线程之间的隔离度比较小,他们共享内存、文件句柄、其他每个进程应有的状态 2、运行效率更高:系统创建进程时候要为其分配系统资源,但创建线程时不需要,所以线程的运行效率更高 3、编程方式简单:JAVA内置了很多多线程功能的支持,并不是简单的对操作系统的底层进行调度,编程更方便。
二、线程的创建方式:
* java的线程模型 1、JAVA中使用Thread类代表线程,所有的线程对象都是必须是Thread类或子类的实例 2、线程用于完成一定的任务(执行一段程序流),JAVA使用线程执行体来表示折断程序流 3、线程的使用过程:定义线程的执行体-->创建线程对象-->调用线程对象的方法启动线程
1、继承Thread类
1、定义Thread类的子类,并重写该类的run()方法(线程体)。 2、创建Thread类的子类的实例,即创建线程对象。 3、调用线程对象的start()方法,启动这个线程。
public static void main(String[] args) {
//子线程
//启动继承与Thread线程
new First().start();
new First().start();//线程支持并发,两个线程并发执行
}
/**
* 继承与Thread方式
*/
private static class First extends Thread{
@Override
public void run() {
//重写线程体
for (int i = 0; i < 5; i ++){
System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
}
}
}
2、实现Runnable接口
1、定义Runnable接口实现类,并实现该接口的run()方法(线程体) 2、创建Runnable实现类的实例,并以此作为target来创建Thread对象 3、调用Thread对象的start()方法启动线程
public static void main(String[] args){
//启动实现Runnable接口线程
new Thread(new Second()).start();
new Thread(new Second()).start();
}
/**
* 实现Runnable接口
*/
private static class Second implements Runnable{
@Override
public void run() {
//重写线程体
for (int i = 0; i < 5; i ++){
System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
}
}
}
3、实现Callable接口
1、定义Callable接口实现类,并实现该接口的call()方法(线程体) 2、创建Callable接口的实例,并使用Future接口来包装Callable对象,最后使用Future对象(线程返回值)作为target来创建Thread对象 3、调用Thread对象的start()方法来启动线程 4、调用Future对象的get()方法获取线程的返回值。
public static void main(String[] args) throws ExecutionException, InterruptedException {
//启动实现Callable接口的线程
FutureTask<String> task1 = new FutureTask<>(new Third());
FutureTask<String> task2 = new FutureTask<>(new Third());
new Thread(task1).start();
new Thread(task2).start();
//启动实现Callable接口的线程的线程名---采用阻塞方法
System.out.println("返回值 - 0 :\t" + task1.get());
System.out.println("返回值 - 1 :\t" + task2.get());
}
/**
* 实现Callable接口
*/
private static class Third implements Callable<String>{
@Override
public String call() throws Exception {
for (int i = 0; i < 5; i ++){
System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
}
return Thread.currentThread().getName();//返回线程名
}
}
4、三种创建线程的比较
* 继承父类的方式:丧失了灵活性 * 优点:编程简单 * 缺点:线程类已经继承了Thread类,所以不能在继承其他的父类 * 实现接口的方式 * 优点:线程类只实现了接口,还可以继承与其他的父类 * 缺点:编程比较麻烦
三、线程生命周期
* 线程的生命周期 * 当线程被创建之后,不会立刻进入运行状态,也不会一直处于运行状态。(如果一直运行,就无法实现并发) * 线程的生命周期要经过5个状态: 1、新建(new) 2、就绪(Ready) 3、运行(Running) 4、阻塞(Blocked) 5、死亡(Dead)
/**
* 线程生命周期
*/
public class demo2 {
public static void main(String[] args) throws InterruptedException {
//启动实现Runnable接口线程
//新建状态
Thread thread0 = new Thread(new Second());
Thread thread1 = new Thread(new Second());
for (int i = 0; i < 20; i ++) {
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
if (i == 5){
//子线程就绪状态,等待被调度
thread0.start();
thread1.start();
// thread1.start();//就绪之后再就绪,会报错,只能对新建状态的线程调用start()
//强制主线程就绪
// Thread.yield();//不确定强制主线程就绪,
//设置主线程阻塞,主线程阻塞1ms,使得thread0,和thread1执行
Thread.sleep(1);
}
}
}
/**
* 实现Runnable接口
*/
private static class Second implements Runnable{
@Override
public void run() {
//重写线程体
for (int i = 0; i < 5; i ++){
System.out.println(Thread.currentThread().getName() + "\t" + i);//输出线程明名
}
}
}
}
四、控制线程手段
1、线程休眠
Thread类提供了休眠方法,可以让当前线程暂停一段时间 1、static void sleep(long millis) 2、static void sleep(long millis,int nanos) sleep()与yield()比较 1、sleep()会让线程进入阻塞状态、而yield()会让线程进入就绪状态 2、sleep()给其他线程运行机会,不理会其他线程的优先级别,yield()考虑线程的优先级,只会给优先级或者优先级更高的线程执行机会。
2、 等待线程
Thread类提供了等待的方法,可以让调用方等待该线程直至他死亡。 * void join() * void join(long millis) * void join(long millis , int nanos)
public static void main(String[] args) throws InterruptedException {
newJoinThread();//Join演示
}
//Join演示
private static class JoinThreadTask implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i ++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
}
}
}
//创建线程
public static void newJoinThread() throws InterruptedException {
//没有Join处理子线程
new Thread(new JoinThreadTask()).start();
//主线程
for (int i = 0; i < 20; i ++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
if (i == 10){
//使用Join方法的子线程
Thread thread = new Thread(new JoinThreadTask());
thread.start();
thread.join();
}
}
}
3、后台线程
* 后台线程、也就守护线程、或者精灵线程。他是在后台运行的,他的任务是为其他线程提供服务的。如果所有的前台线程都死亡,则后台线程会自动死亡 * 线程默认是前台线程,Thread类提供如下方法设置后台线程 * void() setDaemon(boolean on) * boolean inDaemon()
public static void main(String[] args) throws InterruptedException {
newDaemoThread();//后台线程
}
//后台线程
private static class DaemonThreadTask implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i ++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
}
}
}
public static void newDaemoThread(){
//创建一个后台线程
Thread thread = new Thread(new DaemonThreadTask());
thread.setDaemon(true);//设置后台线程
thread.start();
//主线程
for (int i = 0; i < 20; i ++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
}
}
4、线程优先级
* 线程运行时候拥有优先级,优先级高的线程则拥有较多的运行机会 * 线程默认的优先级与它的父线程相同,而主线程具有普通的优先级 * Thread类提供如下成员处理优先级 * static int MAX_PRIORITY; * static int MIN_PRIORITY; * static int NORM_PRIORITY; * int getPriority(); * void setPriority(int newPriority);
public static void main(String[] args) throws InterruptedException {
newThreadPriority();//优先级
}
//优先级
private static class priorityThreadTask implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i ++){
System.out.println(Thread.currentThread().getName() + "\t\t" + i);//输出线程明名
}
}
}
public static void newThreadPriority(){
//创建两个线程用于比较优先级
Thread thread1 = new Thread(new priorityThreadTask());
Thread thread2 = new Thread(new priorityThreadTask());
thread1.setPriority(Thread.MAX_PRIORITY);//优先级设置最大
thread1.start();
thread2.setPriority(Thread.MIN_PRIORITY);//优先级设置最小
thread2.start();
}
五、有关线程安全
1.线程不安全示例
首先展示一个线程不安全的示例,数据共享场景,以一个假话的抢票系统为例
/**
* 线程安全
*/
//模拟抢票系统
public static void main(String[] args){
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task,"Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task,"John").start();
new Thread(task,"Lily").start();
new Thread(task,"张三").start();
new Thread(task,"李四").start();
}
//设计票数
private static class Ticket{
private int amount;//票数
public Ticket(int amount){//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount(){
return amount;
}
public void buy(int amount) {
if (this.amount < amount){
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
//卖票
private static class buyTask implements Runnable{
private Ticket ticket;
public buyTask(Ticket ticket){//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
运行结果,共享数据出现重复
张三购买成功,剩余票数96
Tom购买成功,剩余票数96
Lily购买成功,剩余票数96
李四购买成功,剩余票数96
John购买成功,剩余票数96
2、解决线程安全方法
01、同步代码块
使用synchronized对某一段代码加锁
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//对之下代码加锁,解决数据共享时候的BUG
synchronized (this){
//通过校验成功购票
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
运行结果
John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95
Process finished with exit code 0
02、同步方法块
在方法上加synchronized,此方法比同步代码块差,因为加锁范围增大大,加锁后性能降低
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public synchronized void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
运行结果
John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95
Process finished with exit code 0
03、同步锁
最优解,灵活性高,可以实现读写锁分离
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private Lock lock = new ReentrantLock();//实例化锁对象
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();//表示在此处加锁
try{
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}finally {
lock.unlock();//解锁
}
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
运行结果
John购买成功,剩余票数99
李四购买成功,剩余票数98
Tom购买成功,剩余票数97
张三购买成功,剩余票数96
Lily购买成功,剩余票数95
Process finished with exit code 0
* 以上三种方法是通过加锁方式实现共享数据时候的线程安全问题
六、死锁
当两个线程互相等待对方释放同步监视器时候会发生死锁。
1、死锁示例:
public static void main(String[] args) {
String a = "A";
String b = "B";
new Thread(new FristTask(a,b)).start();
new Thread(new SercodTask(a,b)).start();
}
private static class FristTask implements Runnable{
private Object a;
private Object b;
public FristTask(Object a,Object b){//构造器初始化a,b
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (a){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println("First");
}
}
}
}
private static class SercodTask implements Runnable{
private Object a;
private Object b;
public SercodTask(Object a,Object b){//构造器初始化a,b
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (b){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println("First");
}
}
}
}
程序一直运行,不会输出
2、解决方法
* 避免多次死锁 : 尽量避免同一个线程对多个同步监视器进行锁定
* 按相同的顺序加锁 : 如果多个线程需要对多同步监视器加锁,则应该保证他们以相同的顺序请求加锁
* 使用可以超时释放的锁 : 调用Lock()对象的tryLock(time,unit)方法,当超过指定的时间后他会自动释放锁