线程和进程的基本概念
程序:
? ? ? ?用某种语言编写的一段指令的集合,静态代码静态对象
进程:
? ? ? ?是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:
? ? ? ?是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行
.
一个进程最少有一个线程 ,线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分 成若干个线程
?
并发与并行 &&同步与异步
并发:一个CPU同时执行多个任务
并行:多个CPU同时执行多个不同任务
前者是逻辑上的同时发生,后者是物理上的同时发生。
同步:排队执行 , 效率低但是安全。
异步:同时执行 , 效率高但是数据不安全。
线程的调度方式?
分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)
注意:多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使
用率更高。
? ?
?线程的相关API
//获取当前线程的名字 Thread.currentThread().getName()
1.start():1.启动当前线程2.调用线程中的run方法 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 3.currentThread():静态方法,返回执行当前代码的线程 4.getName():获取当前线程的名字 5.setName():设置当前线程的名字 6.yield():主动释放当前线程的执行权 7.join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去 8.stop():过时方法。当执行此方法时,强制结束当前线程。 9.sleep(long millitime):线程休眠一段时间 10.isAlive():判断当前线程是否存活 ?
多线程的创建方式
1.继承Thread
1.创建一个集成于Thread类的子类 (通过ctrl+o(override)输入run查找run方法) 2.重写Thread类的run()方法 3.创建Thread子类的对象 4.通过此对象调用start()方法
创建线程的方式: MyThread myThread = new MyThread();
2.实现Runnable
相比较Thread而言,通过实现runnable的方式可以更容易地实现资源共享,并且接口可以多实现且还能再继承其他类.
面向接口编程, 松耦合设计 不能独立运行, 需绑定在Thread实例上运行 主线程不能监控子线程何时结束, 也不能获取子线程返回结果 切记启动异步线程的方式是调用start()方法, 而非调用run()方法. 主线程不能捕获子线程的抛出的异常, 通常会在run()方法中包裹一个最大的try-catch,自行处理异常
步骤
1.创建一个实现了Runable接口的类 2.实现类去实现Runnable中的抽象方法:run() 3.创建实现类的对象 4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象 5.通过Thread类的对象调用start()
package com.example.paoduantui.Thread;
public class ThreadDemo01 {
public static void main(String[] args){
window1 w = new window1();
//虽然有三个线程,但是只有一个窗口类实现的Runnable方法,由于三个线程共用一个window对象,所以自动共用100张票
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class window1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket>0){
// try {
// sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票");
ticket--;
}else{
break;
}
}
}
}
?3.实现callable接口方式:
Runnable与Callable
接口定义
//Callable
接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable
接口
public interface Runnable {
public abstract void run();
}
?callable使用步骤
1.
编写类实现
Callable
接口
,
实现
call
方法
class XXX implements Callable<T> {
@Override
? ? ? ? public <T> call() throws Exception {
? ? ? ? ? return T;
? ? ?}
}
2.
创建
FutureTask
对象
,
并传入第一步编写的
Callable
类对象
? ? FutureTask<Integer> future = new FutureTask<>(callable);
3.
通过
Thread,
启动线程
new Thread(future).start();
Runnable与Callable?相同点与不同点
相同:
都是接口
都可以编写多线程程序
都采用
Thread.start()
启动线程
不同:
Runnable
没有返回值;
Callable
可以返回执行结果
Callable
接口的
call()
允许抛出异常;
Runnable
的
run()
不能抛出
注意:
Callalble
接口支持返回执行结果,需要调用
FutureTask.get()
得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
?4.线程池方式
线程池? Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间.
线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
好处:
降低资源消耗。
提高响应速度。
提高线程的可管理性。
?Java中的四种线程池 . ExecutorService
?缓存线程池:ExecutorService service =Executors.newCachedThreadPool();
?定长线程池:ExecutorService service =Executors.newFixedThreadPool();
?单线程线程池:ExecutorService service =Executors.newSingleThreadExecutor();
?周期性任务定长线程池:
ScheduledExecutorService service =?Executors.newScheduledThreadPool();
线程的生命周期:
?
线程通信的常用方法:
?
?
?线程的安全问题:
线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。
?线程安全解决方法:
?方式1:同步代码块:
使用同步监视器(锁) Synchronized(同步监视器){ //需要被同步的代码 }
方式2:同步方法:?
使用同步方法,对方法进行synchronized关键字修饰 将同步代码块提取出来成为一个方法,用synchronized关键字修饰此方法。 对于runnable接口实现多线程,只需要将同步方法用synchronized修饰 而对于继承自Thread方式,需要将同步方法用static和synchronized修饰,因为对象不唯一(锁不唯一) ?
注意
1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。 2.非静态的同步方法,同步监视器是this 静态的同步方法,同步监视器是当前类本身。继承自Thread。class
方式3:显式锁lock:
package com.example.paoduantui.Thread;
import java.util.concurrent.locks.ReentrantLock;
class Window implements Runnable{
private int ticket = 100;//定义一百张票
//1.实例化锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//2.调用锁定方法lock
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出第" + ticket + "张票");
ticket--;
} else {
break;
}
}
}
}
public class LockTest {
public static void main(String[] args){
Window w= new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口1");
t3.setName("窗口1");
t1.start();
t2.start();
t3.start();
}
}
Synchronized与lock的异同?
相同:二者都可以解决线程安全问题 不同:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器 lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(同时以为着lock的方式更为灵活)
优先使用顺序: LOCK->同步代码块->同步方法 ?
|