文章目录
程序 进程 线程的理解
1.程序(program)
是为了完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码.
2.进程(process)
概念:正在运行的程序;
说明:进程是资源分配的单位,每个进程会得到系统分配的不同的内存区域;
3.线程(thread)
概念:线程是进程的进一步细化,是程序执行的一条路径.
说明: 进程可以细化为多个进程
每个线程,拥有自己独立的:虚拟机栈, 程序计数器
多个线程,共享同一个进程中的结构:方法区,堆
一个Java应用程序java.exe,至少有三个线程:main()主线程,垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
4.并行与并发的理解
并行:多个CPU同时执行多个任务;例如:多个人同时做不同的事.
并发: 一个CPU同时执行多个任务;例如:秒杀,多个人同时做一件事.
线程创建的方式
方式一:继承于Thread
1.创建一个继承于Thread类的子类
2.重写Thread中的run()需要执行的操作
3.创建子类对象,调用start();作用:①启动当前线程②调用当前线程的run()
- 问题一:我们不能通过直接调用run()的方式启动线程。
- 问题二:再启动一个线程,不可以让已经start()的程序去执行,会报错IllegalThreadStateException
- 需要重新创建新的线程对象
//1.创建一个继承于Thread类的子类
class MyThread extends Thread {
//2.重写Thread 中的run()
public void run(){
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t=new MyThread();
t.start();
//如下的是在main线程中执行的
for (int i = 0; i < 100; i++) {
if(i%2==0) System.out.println(Thread.currentThread().getName()+i+"****main****");
}
}
}
}
方式二:实现Runnable接口
- 1.创建一个实现接口Runnable的类;
- 2.实现类去实现Runnable中的抽象方法run();
- 3.创建实现类对象
- 4.将此对象作为参数传给Thread中的构造器中,创建Thread对象;
- 5.通过Thread类的对象调用start;①启动线程②调用当前线程的run()–>调用的是Runnable类型的target的run()方法
比较创建线程的两种方式
- 开发中:优先选择:实现Runnable接口的方式
- 原因:1.实现的方式没有类的单继承的局限性
- 2.实现的方式更适合来处理多个线程有共享数据的情况
联系: - public class Thread implements Runnable
- 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
ublic class ThreadTest1 {
public static void main(String[] args) {
Threadjk p = new Threadjk();
Thread t1 = new Thread(p);
t1.setName("线程1");
t1.start();
//再启动一个线程
Thread t2 = new Thread(p);
t2.setName("线程2");
t2.start();
}
}
class Threadjk implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
Thread类中的常用方法
测试Thread中常用的方法
- 1.start():启动当前线程,调用当前线程的run();
- 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明再此方法中
- 3.currentThread():静态方法,返回执行当前代码的线程
- 4.getName():获取当前线程的名字
- 5.setName():设置当前线程的名字
- 6.yield():释放当前CPU的执行权
- 7.join():在线程A中调用线程B的join(),此时线程A进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态;
- 8.stop():已过时,当执行此方法时,强制结束线程
- 9.sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒,在指定的毫秒数内,当前线程处于阻塞状态
- 10.isAlive():判断当前线程是否存活;
1.线程的优先级:
- NORM_PRIORITY = 5;–>默认优先级
- MIN_PRIORITY = 1;
- MAX_PRIORITY = 10;
2.如何获取和设置当前线程的优先级:
- getPriority():获取线程的优先级
- setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着
- 只有高优先级的线程执行结束后,再执行低优先级的线程
线程的生命周期
说明:
1.生命周期关注两个概念:状态、相应的方法
2.关注:状态a–>状态b:哪些方法执行了(回调方法)
某个方法主动调用:状态a–>状态b
3.阻塞:临时状态,不可以作为最终状态
死亡:最终状态。
线程的同步机制
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。
- 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
public void run() {
while(true){
synchronized(this) {//this 表示Dow 对象
if (sticke > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",票号为:" + sticke);
sticke--;
} else {
break;
}
}
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
关于同步方法的总结:
- 1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
- 2.非静态的同步方法,同步监视器是:this
- 3 静态的同步方法,同步监视器是:当前类本身
public synchronized void show(){//同步监视器 this 当前实现类对象
if (sticke > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",票号为:" + sticke);
sticke--;
}
}
方式三:lock(锁)–开发中优先使用 JDK 5.0 新增
- 1.实例化Reentrantlock
- 2.调用lock()锁定方法
- 3.调用unlock()解锁方法
class Windo implements Runnable{
private int ticke=100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();
if (ticke > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":票号:" + ticke);
ticke--;
} else {
break;
}
}finally{
lock.unlock();
}
}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Windo t = new Windo();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
}
}
面试题:synchronized和lock的异同?
- 1.相同点:都能解决线程安全问题
- 2.不同点:synchronized机制在执行完同步代码之后自动释放同步监视器
- lock 需要手动的启动同步(lock()),同时结束同步也需要手动实现(unlock())
面试题:解决线程安全问题有几种方法?
有三种分别是:
- 1.同步代码块
- 2.同步方法
- 3.lock锁
信息通讯测试;
两个线程 交替输出1-100
- 1.wait():一旦执行此方法,当前线程就进入阻塞状态并释放同步监视器
- 2.notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果有多个线程被wait(),就唤醒优先级高的;
- 3.notifyAll():一旦执行此方法,就会唤醒被wait()的所有线程
说明:
1.wait()\notify()\notifyAll()三个方法使用必须在同步代码块内或同步方法内
2.wait()\notify()\notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器
否则会报错:IllegalMonitorStateException
3.wait()\notify()\notifyAll()三个方法声明在Object类中
class Num implements Runnable {
private int p = 1;
@Override
public void run() {
// Object obj=new Object();
while (true) {
synchronized (this) {
notify();
if (p <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + p);
p++;
} else {
break;
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class NumberWait {
public static void main(String[] args) {
Num t = new Num();
Thread j = new Thread(t);
Thread j1 = new Thread(t);
j.start();
j1.start();
j.setName("线程一");
j1.setName("线程二");
}
}
jdk5.0新增创建多线程方式
方式三:实现Callable接口
优势:
1.call()可以有返回值
2.call()可以抛出异常,被外面的操作所捕获,获取异常信息;
3.call()支持泛型
实现步骤:
- 1.创建一个实现Callable的实现类
- 2.实现call()方法,将此线程需要进行的操作声明在call()方法中
- 3.创建Callable实现类的对象
- 4.将此实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
- 5.将FutureTask的对象作为参数传递给Thread类的构造器中,创建Thread对象,并调用start();
- 6.获取Callable中call()方法的返回值
//1.创建一个实现Callable的实现类
class Able implements Callable{
@Override
//2.实现call()方法,将此线程需要进行的操作声明在call()方法中
public Object call() throws Exception {
int sum=0;
for (int i = 1; i <=100; i++) {
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class CallableTest {
public static void main(String[] args) {
//3.创建Callable实现类的对象
Able p = new Able();
//4.将此实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
FutureTask task = new FutureTask(p);//public class FutureTask<V> implements RunnableFuture<V>
//5.将FutureTask的对象作为参数传递给Thread类的构造器中,创建Thread对象,并调用start();
Thread t = new Thread(task);
t.start();
try {
Object sum=task.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
方式四:使用线程池
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
// service.submit(Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown();
}
}
说明:
好处:
- 1.提高响应速度(减少了创建新线程的时间)
- 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没任务时最多保持多长时间后会终止