有序严谨的指令集称为程序。
进程:
程序的同时多运行称为进程
线程:
程序中不同的执行路经称为线程
程序的最小执行单位
多线程:
如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称为”多线程”
多线程的好处:
充分利用cpu的资源 编程简单
简化编程模型 效率高
带来良好的用户体验 易于资源共享
**主线程
Thread类
Java提供了java.lang.Thread类支持多线程编程
主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
多个线程交替执行,不是真正的并行。
线程每次执行时长由分配的CPU时间片长度决定
在Java中创建线程的几种方式
继承Thread类创建线程
//继承Thread类创建线程
public class MyThread extends Thread{
public void run(){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
//需使用start()方法启动线程,如果直接调用线程中run()方法会:
//1.只有主线程一条执行路径2.依次调用了两次run()方法
mt.start();}
实现Runnable接口创建线程
//实现Runnable接口创建线程
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
//创建线程对象
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
}
实现Callable接口创建Thread线程
public class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程:"+Thread.currentThread().getName());
return null;
}
}
public class Test {
public static void main(String[] args) {
FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread());
Thread t = new Thread(ft,"01");
System.out.println("主线程:"+Thread.currentThread().getName());
t.start();
}
}
线程状态:
创建状态
就绪状态
阻塞状态
运行状态
死亡状态
线程调度方法:
setPriority(int newPriority);更改线程的优先级
线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大
MAX_PRIORITY:静态常量 表示最大优先级
MIN_PRIORITY:静态常量 表示最小优先级
sleep(long millis);在指定毫秒数内让当前正在执行的线程休眠
让线程暂时睡眠指定时长,线程进入阻塞状态
睡眠时间过后线程会再进入运行状态
millis为休眠时长,已毫秒为单位
调用sleep()方法需处理InterruptedException异常
join(); 等待该线程终止
Join(long millis);
Join(long millis,int nanos);
使当前线程暂停执行,等待其他线程结束后在继续执行本线程
Millis:已毫秒为单位 的等待时长
Nanos:要等待的附加纳秒时长
需处理InterruptedException异常
yield(); 暂停当前正在执行的线程对象,并执行其他线程
暂停当前线程,允许其他具有相同优先级的线程获运行机会
该线程处于就绪状态,不转为阻塞状态
只是提供一种可能,但是不能保证一定会实现礼让
interrupt(); 中断线程
isAlive();测试线程是否处于活动状态
线程的同步:
线程不同步会遇到的问题:
当多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程会修改数据,造成数据不安全问题。
线程同步:
一段程序在执行过程中无论成功还是失败,其他线程都会等待这段程序执行完毕,才会转入其它线程。
这样可以保证程序的完整性和安全性。
同步方法
使用synchronized修饰的方法控制对类成员变量的访问
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
同步方法:
//模拟网站
public class Site implements Runnable{
//车票总数
private int count = 10;
//当前抢到第几张票
private int num = 0;
//表示车票是否卖完
private boolean falg = false;
@Override
public void run() {
while(!falg){
sale();
}
}
//同步方法实现售票
public synchronized void sale(){
if(count <= 0){
falg = true;
return;
}
//修改数据 (剩余车票总数,抢到第几张票)
count--;
num++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//显示信息,反馈用户抢到第几张票
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票。");
}
}
//模拟多人抢票
public class SiteTest {
public static void main(String[] args) {
Site site = new Site();
Thread t1 = new Thread(site,"小明");
Thread t2 = new Thread(site,"小红");
Thread t3 = new Thread(site,"小绿");
t1.start();
t2.start();
t3.start();
}
}
或者
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
同步代码块:
public void run() {
while(true){
//同步代码块
synchronized(this){
if(count <= 0){
return;
}
//修改数据 (剩余车票总数,抢到第几张票)
count--;
num++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//显示信息,反馈用户抢到第几张票
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票。");
}
}
}
Synchronized()内为需同步的对象,通常为this
效果与同步方法相同
synchronized就是为当前的线程声明一把锁
多个并发线程访问同一资源的同步代码块时
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
线程安全的类型:
ArrayList的add()方法定义
ArrayList类的add()方法为非同步方法
当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题
线程安全与非线程安全区别:
线程安全方法同步
效率较低
适合多线程多线程并发共享资源
非线程安全方法不同步
效率较高
适合单线程开发
常见类型对比
Hashtable 与 HashMap
Hashtable
继承关系
实现了Map接口,Hashtable继承Dictionary类
线程安全,效率较低
键和值都不允许为null
HashMap
继承关系
实现了Map接口,继承AbstractMap类
非线程安全,效率较高
键和值都允许为null
StringBuffer 与 StringBuilder
前者线程安全,后者非线程安全
死锁:两个线程都在等待对方先完成,造成程序的停滞
死锁的条件:
两个或两个以上的线程在活动
某个线程拿到一个锁以后,还想拿第二个锁,造成锁的嵌套
线程池:
尽量保证任务数不要超过最大线程数+阻塞队列的长度
使用线程池的好处:
重用存在的线程,减少对对象创建,消亡的开销
有效控制最大并发数,提高系统资源使用率
定时执行,定期执行
线程池所在的包:
Java.util.concurrent
顶级接口Executor,真正的线程池接口时ExecutorService
Java.util.concurrent.Executors类提供创建线程池的方法
创建线程池的方法:
newCachedThreadPool();创建一个可缓存的线程池,有任务时才创建新任务
/**
* 使用newCachedThreadPool()方法创建线程池
* */
public class Test {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
//在线程池中执行10个任务
for (int i = 0; i < 10; i++) {
pool.execute(new MyRunnable(i));
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable() {
super();
}
public MyRunnable(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
newSingleThreadExecutor();创建一个单线程池
/**
* 使用newSingleThreadPool()方法创建线程池
* */
public class Test2 {
public static void main(String[] args) {
ExecutorService pool = Executors.newSingleThreadExecutor();
//在线程池中执行10个任务
for (int i = 0; i < 10; i++) {
pool.execute(new MyRunnable(i));
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable() {
super();
}
public MyRunnable(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
newFixedThreadPool(int nThread)创建一个固定长度的线程池,空闲线程会一直保留。参数nThread设定线程池中线程的数目
/**
* 使用newFixedThreadPool()方法创建线程池
* */
public class Test {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
//在线程池中执行10个任务
for (int i = 0; i < 10; i++) {
pool.execute(new MyRunnable(i));
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable() {
super();
}
public MyRunnable(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
newScheduledThreadPool(int corePoolSize)创建一个固定长度的线程池,而且已延迟或定时的方式来执行任务
/**
* 使用newScheduledThreadPool()方法创建线程池
* */
public class Test {
public static void main(String[] args) {
ScheduledExecutorService service= Executors.newScheduledThreadPool(3);
service.scheduleAtFixedRate(new MyRunnable(), 1, 2, TimeUnit.SECONDS);
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"延时5秒执行,每2秒执行一次");
}
}
ThreadPoolExecutor类构造器各个参数的含义:
**corePoolSize:核心池的大小
maximumPoolSize:线程池最大线程数
KeepAliveTime:表示线程没有任务执行时最多保持多久时间会中止
unit:参数KeepAliveTime的时间单位
workQueue:一个阻塞队列,用来存储等待执行的任务
threadFactory:线程工厂,主要用来创建工厂
Handler:表示当拒绝处理任务时的策略**
自定义线程池:
/**
*
* 创建自定义线程池
*
* */
public class Test {
public static void main(String[] args) {
//创建自定义线程池 尽量保证:任务数不要超过最大线程数+阻塞队列的长度
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,7,300,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(4));
for(int i=0;i<12;i++){
executor.execute(new MyRunnable(i));
System.out.println("线程池中线程数:"+executor.getPoolSize()+",对列中等待任务执行数:"+executor.getQueue().size()+",已经执行完的任务数:"+executor.getCompletedTaskCount());
}
executor.shutdown();
}
}
class MyRunnable implements Runnable{
int num;//第几个任务
public MyRunnable(int num) {
super();
this.num = num;
}
@Override
public void run() {
System.out.println("正在执行任务"+num);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务"+num+"执行完毕");
}
}
后台线程的特点:
随着前台线程的运行而运行
随着前台线程的消亡而消亡
SetDaemon():将线程设置为后台线程 GC 传入为true是将线程设置为后台线程,默认false
isDaemon():判断是否是后台线程