IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java多线程概述 -> 正文阅读

[Java知识库]Java多线程概述

笼统的介绍我了解的多线程:首先Java中的多线程是抢占式(线程的调度策略),并发执行(在同一个时间段内执行)的。

一、线程的状态

在jdk的API中线程的状态有如下六种:

二、两种常用的创建线程的方式

????????NEW(新的,在Java中创建对象时的关键字,可以理解为是编程语言中的创建创造),线程处于这一状态,无非就是我们程序员对线程的编写。

????????创建线程的方式有三种:继承Thread类、实现Runnable接口、使用Callable和Future创建线程。

方式一:继承Thread类( extends Thread),并重写run()方法

public class ThreadTest extends Thread{
	private int i;
	//重写run()方法,run()方法的方法体就是线程的执行体
	public void run() {
		for(;i<100;i++) {
			//当线程继承Thread类时直接使用this即可获取当前进程
			//Thread对象的getName()方法返回当前线程的名字
			//因此可以直接调用getName()方法返回当前线程的名字
			System.out.println(getName()+" "+i);
		}
	}

	public static void main(String[] args) {
		//main方法其实也是一个线程,更是线程中的主线程
		for(int i=0;i<100;i++) {
			//调用Thread的currentThead()方法获取当前线程
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==20) {
				//创建并启动第一个线程
				//start()启动该线程的run()方法即线程执行体
				new ThreadTest().start();
				//创建并启动第二个线程
				new ThreadTest().start();
			}
		}

	}

}

?

方式二: 实现Runnable接口,并重写run()方法

实现Runnable的形式创建线程:

1--创建一个任务对象。实现Runnable,重写run()方法

2--创建一个线程,并为其分配一个任务

3--执行这个线程

实现Runnable 与 集成Thread相比存在的优势:

1--通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况

2--可以避免单线程继承带来的局限性。(可以实现多个接口,可以继承一个父类)

3--任务与线程本身分离,提高了程序的健壮性

4--后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程

?

public class RunnableTest implements Runnable{
	private int i;
	//run()方法 同样是线程的执行体
	public void run() {
		/*
		 * 用于给线程执行的任务
		 */
		for(;i<100;i++) {
			//当前线程实现接口时
			//如果获取当前线程,只能用Thread。currentThread()方法
			//这里是实现Runnable接口,而不是继承Thread,所以要用Thread。currentThread()方法获取当前的线程
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++) {
			//调用Thread的currentThead()方法获取当前线程
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==20) {
				RunnableTest st=new RunnableTest();
				
				//创建并启动第一个线程
				//start()启动该线程的run()方法即线程执行体
				//Thread的作用是把run()方法包装成线程的执行体
				new Thread(st,"新线程1").start();
				//床架并启动第二个线程
				new Thread(st,"新线程2").start();
			}
		}

	}

}

?

主线程的线程名需要获取主线程,在获得当前线程的名字的形式得到:

Thread.currentThread():获取当前线程,当线程继承Thread类时,在run()方法中直接使用this即可获取当前进程(this在类内可以省略)。但是除此之外的情况:在主线程中获取当前线程、在Runnable的实现类的任务方法中,必须使用Thread.currentThread()方法。

getName():获取线程的名字,在获取当线程后可以通过此方法获得当前线程的名字。

三、线程的阻塞和中断

线程阻塞(耗时操作):所有消耗时间的操作

例如:读取文件,导致线程等待,除非文件读取完毕才能继续执行该线程

线程中断:一个线程是一个独立执行的路径,他是否结束,应该由其自身决定。

线程分为守护线程和用户线程

用户线程:当一个进程不包含任何的存活的用户线程(直接创建的线程都是用户线程,及主线程)时。进行结束

守护线程:守护用户的线程,当最后一个用户线程结束时,所有守护线程自动死亡。设置守护线程:线程对象调用setDaemon(true)方法,true参数为开启守护线程--要在线程启动之前设置。

public class Interrupt {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Thread t = new Thread(new MyRunnable());
//		//设置守护进程--要在线程启动之前设置
//		t.setDaemon(true);
		t.start();
		for(int i = 0;i <= 10;i++) {
			System.out.println(Thread.currentThread().getName()+""+i);
			try {
				Thread.sleep(1000);//等待1秒钟
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		//给线程t添加中断标记--在特殊操作室检查中断标记,发现时抛出异常进入catch块
		/*
		 * 打标记后知识进入catch块而已,是否真正的死亡,看程序员在catch块的操作
		 * 不进行死亡操作,catch块的内容为空
		 * 死亡操作:直接return;
		 */
		t.interrupt();
		

	}
	
	static class MyRunnable implements Runnable{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			for(int i = 0;i <= 10;i++) {
				System.out.println(Thread.currentThread().getName()+""+i);
				try {
					Thread.sleep(1000);//等待1秒钟
				}catch(InterruptedException e) {
					//e.printStackTrace();
					System.out.println("发现了中断标记,线程自杀");
					return;
				}
			}
		}
		
	}

}

四、线程锁

在了解线程锁之前,先了解线程安全问题:多个线程同时运行同一个任务时,容易发生线程不安全的问题。

下面的代码模拟多个线程访问一个任务致使在数据访问时发生异常

public class NoSecurity {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Runnable run = new Ticket();
		Thread t1 = new Thread(run,"线程1");
		t1.start();
		Thread t2 = new Thread(run,"线程2");
		t2.start();
		Thread t3 = new Thread(run,"线程3");
		t3.start();
	}
	
	
	static class Ticket implements Runnable{

		//票数
		private int count = 10;
		@Override
		public void run() {
			//卖票
			while(count > 0) {//循环使得一个线程的票全部卖完为止
				System.out.println(Thread.currentThread().getName()+"正在准备卖票");
				
				//该线程在准备秒票钱进入睡眠使其他线程运行,导致该线程数据出错
				/*
				 * 因为其他线程的介入,导致count进入循环前的数据跟count--的数据不一致
				 * (三个线程同时进入循环,因为时间篇的丢失,count--运算的数据不正常)
				 */
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				count--;
				System.out.println(Thread.currentThread().getName()+"卖票成功,余票" + count);
			}
		}
	}
}

?解决方案:使用线程锁,在Java中有三种锁的创建形式。按照分类可以分为:

显示锁:同步锁(Lock)

隐式锁:同步代码块,同步方法

同步代码块:

**
 * 解决线程不安全问题:
 * 		在线程执行任务的数据运算之前,不允许其他线程的插足、介入----排队执行
 * 		实现线程的同步--加锁
 * 
 * 
 * 	解决方案1--同步监视器的通用方法“同步代码块”(简单认为括住的内容是会排队的)
 * 		在数据之前进行上锁
 * 
 * 		同步代码块的对象只能是引用数据类型
 * 		多个代码线程使用同一把锁--所以锁对象的创建应该在任务之前
 * 		
 * @author jiazikang
 *
 *线程1先执行的抢到的几率更高
 */
public class Solve_Synchronized {

	public static void main(String[] args) {
		Runnable run = new Ticket();
		Thread t1 = new Thread(run,"线程1---");
		t1.start();
		Thread t2 = new Thread(run,"线程2---");
		t2.start();
		Thread t3 = new Thread(run,"线程3---");
		t3.start();
	}
	
	
	static class Ticket implements Runnable{

		//票数
		private int count = 10;
		private Object o = new Object();
		@Override
		public void run() {
			//卖票
			while (true) {
				synchronized (o) {
					if (count > 0) {
						System.out.println(Thread.currentThread().getName() + "正在准备卖票");
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						count--;
						System.out.println(Thread.currentThread().getName() + "卖票成功,余票" + count);
					
					}else {
						break;
					}
				}
			}
		}
		
	}

}

?主要操作步骤:1.创建锁对象private Object o = new Object();

? ? ? ? ? ? ? ? ? ? ? ? ? ?2.使用synchronized (o) {}形式的代码块

同步方法?:

/**
 * 解决方案2同步方法
 * 同步方法:
 * 		编写一个以synchronized关键字作为修饰符的方法,在线程的任务中调用该方法
 * 
 * 		锁为:调用该方法(任务方法)的this、
 * 		如果同步方法为静态的话,调用该方法的为:类名.class
 * @author jiazikang
 *
 */
public class Solve_SynchronizedWay {

	public static void main(String[] args) {
		Runnable run = new Ticket();
		Thread t1 = new Thread(run,"线程1---");
		t1.start();
		Thread t2 = new Thread(run,"线程2---");
		t2.start();
		Thread t3 = new Thread(run,"线程3---");
		t3.start();
	}
	
	
	static class Ticket implements Runnable{

		//票数
		private int count = 10;
		private Object o = new Object();
		
		private synchronized boolean sale() {
			if (count > 0) {
				System.out.println(Thread.currentThread().getName() + "正在准备卖票");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				count--;
				System.out.println(Thread.currentThread().getName() + "卖票成功,余票" + count);
				return true;
			}
			return false;
		}
		@Override
		public void run() {
			//卖票
			while (true) {
				Boolean flag = sale();
				if(!flag) {
					break;
				}
					
				
			}
		}
	}
}

?主要操作步骤:编写使用synchronized关键字修饰的方法,对想要加锁的内容进行封装

同步锁?:

主要操作步骤:

1.定义锁对象

private final ReentrantLock lock = new ReentrantLock();

2.调用对象的加锁方法:lock.lock();

3.调用对象的开锁方法:lock.unlock();

代码与上面内容基本相同,加锁内容的方法或者代码块的执行体开始部分使用lock.lock();执行体的要结束地方使用lock.unlock();

五、线程通信

实质:多个线程之间的交流

大致思路:

1.创建多个线程

2.创建一个当前线程操作结束的标识,根据等待和唤醒操作为标识设置相应的值

等待:wait();? ? ? ? ? ? ? ?唤醒:notifyAll();

3.在一个锁内完成交流操作(保证线程的安全和数据的正确)

public class ThreadCommunication  {

    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:36:58  更:2021-07-30 12:38:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 0:39:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码