是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码
,静态对象
是程序的一次执行过程,或是正在运行的一个程序
。是一个动态的过程:有它自身的产生、生存和消亡的过程–声明周期
进程可进一步细化为线程,是一个程序内部的一条执行路径
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC)
,线程切换的开销小安全的隐患
…
一个Java应用程序,至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当异常发生是,会影响主线程
多个CPU同时执行多个任务。比如:多个人同时做不同的事
一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
线程的创建和使用
步骤:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
步骤:
开发中,优先选择实现Runnable接口的方式
原因:
联系:public class Thread implements Runnable(Thread本身也是实现的Runnable接口)
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
线程的生命周期通常经历五种状态:新建、就绪、运行、阻塞、死亡
当一个Thread类或其子类的对象被声明并创建时,新生的线程对像处于新建状态
处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
在某种特殊情况下,被认为挂起
或执行输入输出
操作时,让出CPU并临时中止自己的执行,进入阻塞状态
线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
线程的同步
线程同步主要就是解决线程安全的问题
synchronized(同步监视器){//需要被同步的代码}
说明:
要求:多个线程必须公用同一把锁
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,可以考虑使用当前类(当前类的类名.class)充当同步监视器
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
@Overridepublic void run() {while(true){show();}}public synchronized void show(){//这里的同步监视器是:this
// public void show(){
// synchronized(this){if(ticket > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);ticket--;}//没有break用于跳出上面的while循环}}
静态的同步方法,同步监视器是:当前类本身
@Overridepublic void run() {while(true){show();}}public static synchronized void show(){//同步监视器:Window4.class。类本身if(ticket > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);ticket--;}//没有break用于跳出上面的while循环}
同步的方式,解决了线程的安全问题。—>好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。—>局限性
/**** 使用同步机制将单例模式中的懒汉式改写为线程完全的** @author LawrenceLan* @create 2022--09--22--21:45*/
public class BankTest {
}class Bank {private Bank() {}private static Bank instance = null;private static Bank getInstance() {//方式一:效率稍差
// synchronized(Bank.class){
// if(instance == null){
// instance = new Bank();
// }
// return instance;
// }//方式二:效率更高if (instance == null) {synchronized (Bank.class) {if (instance == null) {instance = new Bank();}}}return instance;}
}
互斥条件
:该资源在任意时刻都只由一个线程占用请求与保持条件
:一个线程在申请某个资源时产生了阻塞,而已占有的资源保持不放不剥夺条件
:线程在已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源循环等待条件
:若然进程之间形成一种头尾相接的循环等待资源关系专门的算法、原则
尽量减少同步资源的定义
尽量避免嵌套同步
破坏互斥条件
:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)破坏请求与保持条件
:一次性申请所有的资源破坏不被剥夺条件
:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源破坏循环等待条件
:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。步骤:
import urrent.locks.ReentrantLock;/**** 解决线程完全问题的方式三:Lock锁---JDK5.0新增** @author LawrenceLan* @create 2022--09--22--22:46*/class Window implements Runnable{private int ticket = 1000;//1、实例化ReentrantLockprivate ReentrantLock lock =new ReentrantLock();@Overridepublic void run() {while(true){try{//2.调用lock()方法。线程进来后,锁定,保证一次只有一个线程进代码块来lock.lock();if(ticket > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);ticket--;}else{break;}}finally {//3.调用unlock()方法。解锁lock.unlock();}}}
}public class LockTest {public static void main(String[] args) {Window w1 = new Window();Thread t1 =new Thread(w1);Thread t2 =new Thread(w1);Thread t3 =new Thread(w1);t1.setName("窗口1:");t2.setName("窗口2:");t3.setName("窗口3:");t1.start();t2.start();t3.start();}
}
面试题:synchronized和Losk的异同?
相同:二者都可以解决线程安全问题
不同:
涉及到三个方法:
一个
线程。如果有多个线程被wait,就唤醒优先级高的那个所有
被wait()的线程说明:
面试题:sleep()和wait的异同?
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
不同点:
步骤:
import urrent.Callable;
import urrent.ExecutionException;
import urrent.FutureTask;/*** @author LawrenceLan* @create 2022--09--23--11:45*///1.创建要给实现Callable的实现类
class NumThread implements Callable{//2.实现call方法,将此线程需要执行的操作声明在call()中。并且可以返回一个值@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {if(i % 2 == 0 ){System.out.println(i);sum += i;}}return sum;}
}public class ThreadNew {public static void main(String[] args){//3.创建Callable接口实现类的对象NumThread numThread = new NumThread();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象FutureTask futureTask = new FutureTask(numThread);//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()new Thread(futureTask).start();try {//6.获取Callable中call方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值Object sum = ();System.out.println("总和为:"+sum);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}
背景:对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:
import urrent.ExecutorService;
import urrent.Executors;
import urrent.ThreadPoolExecutor;/**** 创建线程的方式四:使用线程池** @author LawrenceLan* @create 2022--09--23--14:20*/class NumberThread implements Runnable{@Overridepublic void run() {for (int i = 0; i <= 100 ; i++) {if(i % 2 == 0){System.out.println(Thread.currentThread().getName()+":"+i);}}}
}class NumberThread1 implements Runnable{@Overridepublic 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 = wFixedThreadPool(10);ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;// 向子类强制转型,才能调用子类的方法//设置线程池的属性
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象ute(new NumberThread());//适合使用于ute(new NumberThread1());//适合使用于Runnable// service.submit();//适合使用于Callable//3.关闭线程池service.shutdown();}
}
本文发布于:2024-01-30 01:13:31,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170654841218179.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |