1、线程与进程
进程:运行中的程序称为进程 (每一个软件启动都对应一个进程)
线程:进程中的一条执行流(进程中可以同时有多个执行流 –多线程 —-多线程的应用程序)
多线程的应用程序,程序可以并行执行多个任务(操作),相对单线程应用程序,效率要高很多;
2、创建线程
在JAVA语言,提供4种线程的创建方式,常见的主要是2种(书本上的)
方式一: 继承Thread类创建一个线程 (只需要重写run方法即可)
public class Thread1 extends Thread {
public Thread1(String name) {
this.setName(name);
}
@Override
public void run() { //当前线程要执行的操作;
for(int i=1;i<10;i++) {
System.out.println(Thread.currentThread().getName() +" -> " + i);
}
}
}
复制代码
启动线程:不能为run() ,而是start()
方式二:实现Runnable接口创建线程
public class Thread2 implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(Thread.currentThread().getName() +" -> " +i);
}
}
}
复制代码
方式三:实现Callable接口创建线程
public class Thread3 implements Callable<String> {
@Override
public String call() throws Exception {
String val ="call";
//......
return val;
}
}
Callable<String> call = new Thread3();
FutureTask<String> future = new FutureTask<String>(call);
Thread t = new Thread(future);
t.start();
String val = future.get();
System.out.println("val ->" + val);
复制代码
实现Callable接口创建的线程,运行的结果被打包到一个FutureTask对象中,通过此对象的get方法得到结果;如果返回值为String,则对象为FutureTask对象;如果为Integer ,则结果为FutureTask对象,如果返回值为Double ,则结果为:FutureTask类的对象……
方式四:基于**线程池(ThreadPool)**创建线程并执行
线程池(Thread Pool) :是一个线程的容器,当将线程对象放到池中,线程池会自动为此线程对象创建一个线程并在合适的时机执行,线程池有一个优点,线程执行完后,返回线程池,避免重复创建线程带的开销;
字符池 | 线程池 | 连接池 | …….
Executors
ThreadPoolExecutor
复制代码
通过java.util.concurrent.Executors类创建线程池
//1、Executors创建存放单个线程的线程池 ;
ExecutorService executorService = Executors.newSingleThreadExecutor();
//在线程池中放线程:通过execute方法放线程(没有返回值)| 通过submit方法向池中添加线程,有返回值;
executorService.submit(new ThreadA());
if(executorService.isShutdown()==false) {
executorService.shutdown();
}
//2、Executors创建存放固定数量的线程池 ;
ExecutorService executorService =Executors.newFixedThreadPool(5);
for(int i=0;i<5;i++) {
executorService.submit(new ThreadA());
}
//3、Executors创建存放不确定数量的线程池 ;(任意)
ExecutorService executorService = Executors.newCachedThreadPool();
//4、Executors创建作业调度线程池(在指定时间执行或者重复执行)
ScheduledExecutorService schedudService =Executors.newScheduledThreadPool(1);
schedudService.schedule(new ThreadA(), 5, TimeUnit.SECONDS);
ScheduledExecutorService schedudService =Executors.newScheduledThreadPool(1);
schedudService.scheduleAtFixedRate(new ThreadA(), 10, 3, TimeUnit.SECONDS);
复制代码
ThreadPoolExecutor创建池
//corePoolSize:核心池大小
// maximumPoolSize:最大数量
//BlockingQueue<Runnable>:队列(存放任务):new ArrayBlockingQueue<Runnable>(10)|LinkedBlockingDeque<Runnable>()|......
ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
pool.submit(new ThreadA());
pool.shutdown();
复制代码
继承Thread类创建线程与实现Runnable接口创建线程区别?
实现接口可以避免java中的单继承的限制;
对共享资源的访问通过接口实现相对继承类实现更合适
实现接口相对继承类更能体现面向接口编程
线程池只能放入实现Runable或callable线程,不能直接放入继承Thread类创建的线程
......
复制代码
实现Runnable接口创建线程与Callable接口创建线程的区别?
Runnable接口中定义的run方法没有返回值;Callable中的call方法有返回值
call方法相对run方法多了一个异常处理
复制代码
3、线程控制
启动线程 start() (不是run())
停止线程 stop() -- 已声明为过时的方法(放弃),如果要停止线程,需要用其它手段
线程优先级:获取优先级 | 设置优先级 (线程优先高的并不一定比线程优先级低的线程百分百先抢到CPU资源)
getPriority() --- [1,10] | setPriority(int)
线程休眠 Thread.sleep() 线程将进入阻塞(等待),一旦休眠时间一到,会自动进入就绪状态(可以挣抢CPU资源)
Thread.yield() :让出CPU资源(只让给比自己优先级高的线程),让出后,立刻进入就缓状态(挣抢CPU资源)
线程中断(针对线程休眠) interrupt(),唤醒休眠中线程(会抛异常)
判断线程是否为活的线程:isAlive()
线程等待join() :一旦某一个线程的join方法被调用,其它线程需要等待当前线程执行完,再执行;
Runnable rn = new ThreadC();
Thread t1 = new Thread(rn, "第一步:验证");
Thread t2 = new Thread(rn, "第二步:实现登录");
Thread t3 = new Thread(rn, "第三步:记录日志");
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
public class ThreadC implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
线程名称 :setName(String);
守护线线程:后台线程(GC是属于守护线程)
复制代码
4、线程状态
新建 —-start() —>就绪状态(可以挣抢CPU资源) —-抢到 —>运行状态 ( — 阻塞状态 —–就绪状态 — 挣抢CPU资源 — 运行) ——> 终止
5、线程同步(锁)
当多个线程访问同一资源,怎么解决数据一致性问题(有可能发生,也有可能不会发生)? 要确保一次只能让一个线程去访问此数据,等此线程操作结束,再允许其它线程访问共享资源
让某一资源一次只允许一个线程访问? —- > 线程锁(加锁来进行访问)
锁:排它的
线程同步 :一次只允许某一个线程对某一资源进行访问称为线程同步
同步代码块
synchronized (lock) {
while(data < 10) {
//线程1阻塞状态;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
data ++;
System.out.println(Thread.currentThread().getName() +" -> " + data);
}
}
synchronized :自动加锁、自动解锁 ---- 悲观锁
synchronized:可以做:同步方法 | 同步代码块
复制代码
锁:能不能手动添加|手动解除
public interface Lock { //接口定义了手动加锁操作规范
void lock(); //加锁
void unlock(); //解锁
//.....
}
public class ReentrantLock implements Lock{ //此接口的唯一实现类
}
lock.lock();
while(data < 10) {
//线程1阻塞状态;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
data ++;
System.out.println(Thread.currentThread().getName() +" -> " + data);
}
lock.unlock();
复制代码