程序(Program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。
--程序是静态的,进程是动态的
--进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread):进程可以进一步细化为线程,是一个程序内部的一条执行路径。
--若一个进程同一时间并行执行多个线程,就是支持多线程的
--线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
--一个进程中的多个线程共享相同的内存单元/内存地址空间-->他们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源就可能会带来安全隐患。
>单核CPU,是一种假的多线程,因为一个时间单元内,只能执行一个线程的任务。
>如果是多核的话,才能发挥多线程的效率
>一个JAVA应用程序java.exe,其实至少有三个线程:main()主线程、gc()垃圾回收线程、异常处理线程。如果发生异常,会影响主线程。
>并行:多个CPU同时执行多个任务。
>并发:一个CPU(采用时间片)同时执行多个任务。
1、提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2、提高计算机系统CPU利用率。
3、改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
程序需要同时执行两个或多个任务
程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等
需要一些后台运行的程序时
调度策略:时间片-抢占式
JAVA的调度方法:
>同优先级线程组成先进先出队列(先到先服务),使用时间片策略
>对高优先级,使用优先调度的抢占式策略
高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
多线程的创建,方式一:继承于Thread类 * 1、创建一个继承于Thread类中的子类 * 2、重写Thread类的run方法-->将此线程执行的操作声明在run方法中 * 3、创建Thread类的子类的对象 * 4、通过此对象调用start()
创建多线程的方式二:实现Runnable接口
* 1、创建一个实现了Runnable接口的类
* 2、实现类去实现Runnable中的抽象方法:run()
* 3、创建实现类的对象
* 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5、通过Thread类的对象调用start()
*
比较创建线程的两种方式:
*开发中优先选择实现Runnable接口的方式
* 原因:
* 1、实现的方式没有类的单继承的局限性
* 2、实现的方式更适合来处理多个线程有共享数据的情况
* 联系: public class Thread implements Runnable
* 相同点:两种方式都需要重写run方法,将线程要执行的逻辑声明在run()方法中
解决线程安全问题的三种方法:
方式一:同步代码块
* synchronized(同步监视器){
* //需要被同步的代码
* }
* 说明:操作共享数据的代码,即为需要被同步的代码-->不能包多了,也不能包少了,
* 共享数据:多个线程共同操作的变量。比如:tickets
* 同步监视器:俗称锁。任何一个类的对象,都可以充当锁。obj
* 要求:多个线程必须公用一把锁
* 简单方法:用当前对象对充当
* 补充:在实现runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器。
*
方式二:同步方法
*如果操作共享数据的代码完整的声明在一个方法中,可以考虑使用this充当同步监视器
*
* 5、同步的方式,解决了线程的安全问题。--好处
* 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。--局限性方式三:LOCK锁----JDK5.0新增
*
*1、面试题:synchronized 和 LOCK锁的异同?
* 相同点:
* 不同:synchronized机制在执行完相同的同步代码之后,自动释放同步监视器
* lock需要手动启动同步(lock()),同时结束同步也需要手动的实现(unlock())
* 2、面试题:如何解决线程安全问题?有几种方式:两种/三种
* synchronized方法、synchronized代码块、lock代码块锁利弊:
同步的方式,解决了线程安全的问题。--好处
操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。--坏处
package com.atguigu.java;
/**
* 例子:创建三个窗口买票,总票数为100张,使用Runnable接口的方式
* 存在线程安全问题,待解决
* 1、问题一:买票过程中出现了重票、错票
* 2、原因:当某个线程来操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
* 3、解决:当一个线程在操作共享数据的时候,其他线程不能参与进来,直到这个线程操作完此数据时,其他线程才可以开始操作此数据,即使这个线程出现了阻塞,其他线程也不能因此参与进来
* 4、在JAVA中,通过同步机制来解决线程安全问题
* 方式一:同步代码块
* synchronized(同步监视器){
* //需要被同步的代码
* }
* 说明:操作共享数据的代码,即为需要被同步的代码-->不能包多了,也不能包少了,
* 共享数据:多个线程共同操作的变量。比如:tickets
* 同步监视器:俗称锁。任何一个类的对象,都可以充当锁。obj
* 要求:多个线程必须公用一把锁
* 简单方法:用当前对象对充当
* 补充:在实现runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器。
*
* 方式二:同步方法
*如果操作共享数据的代码完整的声明在一个方法中,可以考虑使用this充当同步监视器
*
* 5、同步的方式,解决了线程的安全问题。--好处
* 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。--局限性
*
* @author gaixinyu
* @create 2021-03-24-21:21
*/
public class WindowTest1 {
public static void main(String[] args) {
Window1 w=new Window1();
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 tickes=100;//不需static
Object obj=new Object();
@Override
public void run() {
while (true){
synchronized (this){//同步代码块,此时的this是唯一的windows1的对象,这是最方便的,不需要new一个object
if(tickes>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":买票,票号为:"+tickes);
tickes--;
}else {
break;
}
}
}
}
}
package com.atguigu.java;
/**
* 例子:创建三个窗口买票,总票数为100张,使用继承的方式
* 存在线程安全问题,待解决
* 使用同步代码块来解决继承Thread类的线程安全问题:
*说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视起,考虑使用当前类充当同步监视器。
* @author gaixinyu
* @create 2021-03-24-20:53
*/
public class WindowTest2 {
public static void main(String[] args) {
Window2 t1=new Window2();
Window2 t2=new Window2();
Window2 t3=new Window2();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window2 extends Thread{
private static int tickets=100;//static避免重复
// Object obj=new Object();//这时候有三个锁
private static Object obj=new Object();
//因为造了多个对象
@Override
public void run() {
while(true){
//正确的
synchronized (obj){
//类也可以充当。类也是对象
//synchronized (Window2.class){//Window2只会加载一次,所以唯一
//错误的,因为此时的this代表着t1,t2,t3三个对象,不唯一
// synchronized (this){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":买票,票号为:"+tickets);
tickets--;
}else {
break;
}
}
}
}
}
package com.atguigu.java;
/**
* 使用同步方法解决实现Runnable接口的线程安全问题
*
* 关于同步方法的总结:
* 1、同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
* 2、非静态的同步方法,同步监视器是:this
* 3、静态的同步方法,同步监视器是:当前类本身
*
* @author gaixinyu
* @create 2021-03-25-15:53
*/
public class WindowTest3 {
public static void main(String[] args) {
Window3 w=new Window3();
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 Window3 implements Runnable{
private int tickes=100;//不需static
Object obj=new Object();
@Override
public void run() {
while (true){
show();
}
}
//同步监视器:this
private synchronized void show(){//完整的操作共享数据的方法
if(tickes>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":买票,票号为:"+tickes);
tickes--;
}
}
}
package com.atguigu.java;
/**
* 使用同步方法来处理继承Thread类的线程安全问题:
* @author gaixinyu
* @create 2021-03-25-15:59
*/
public class WindowTest4 {
public static void main(String[] args) {
Window4 t1=new Window4();
Window4 t2=new Window4();
Window4 t3=new Window4();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window4 extends Thread{
private static int tickets=100;//static避免重复
@Override
public void run() {
while(true){
show();
}
}
private static synchronized void show(){//同步监视器:Window4.class
//private synchronized void show(){//此种解决方式是错误的
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":买票,票号为:"+tickets);
tickets--;
}
}
}
package com.atguigu.java1;
import java.util.concurrent.locks.ReentrantLock;
/**
* 解决线程安全问题的方式三:LOCK锁----JDK5.0新增
*
*1、面试题:synchronized 和 LOCK锁的异同?
* 相同点:
* 不同:synchronized机制在执行完相同的同步代码之后,自动释放同步监视器
* lock需要手动启动同步(lock()),同时结束同步也需要手动的实现(unlock())
* 2、面试题:如何解决线程安全问题?有几种方式:两种/三种
* synchronized方法、synchronized代码块、lock代码块锁
*
* @author gaixinyu
* @create 2021-03-25-18:58
*/
class Windows implements Runnable{
private int ticket=100;
//1、实例化ReentrantLock
private ReentrantLock lock=new ReentrantLock(true);
@Override
public void run() {
while (true){
try {
//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;
}
}finally {
//3、调用解锁方法:
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Windows w=new Windows();
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();
}
}
package com.atguigu.java1;
/**
* 使用同步机制将单例模式中的懒汉式改写成线程安全的
* @author gaixinyu
* @create 2021-03-25-16:09
*/
public class BankTest {
}
class Bank{
public Bank() {
}
private static Bank instance=null;
public static synchronized 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;
}
}
三个方法:
package com.atguigu.java2;
/**
* 线程通信的例子
* 涉及到的方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
* notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的线程
* notifyAll():一旦执行此方法,就会唤醒被wait的所有线程。
*
* 说明:
* 1、wait()、notify()、notifyAll()三个方法都必须使用在同步方法或同步代码块中
* 2、三个方法的调用者必须是同步代码块或者同步方法中的同步监视器,否则,会出现异常
* 3、三个方法是定义在java.lang.object类中的
* @author gaixinyu
* @create 2021-03-25-19:51
*/
public class Communication {
public static void main(String[] args) {
Number number=new Number();//注意不同
Thread n1 = new Thread(number);//
Thread n2 = new Thread(number);//
n1.setName("线程1");
n2.setName("线程2");
n1.start();
n2.start();
}
}
class Number implements Runnable{
private int number=1;
@Override
public void run() {
while (true){
synchronized (this) {//同步代码块
//唤醒
notify();//互相唤醒
if(number<=100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+number);
number++;
try {//使得调用如下wait方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
1、面试题:synchronized 和 LOCK锁的异同?
相同点:
不同:synchronized机制在执行完相同的同步代码之后,自动释放同步监视器
lock需要手动启动同步(lock()),同时结束同步也需要手动的实现(unlock())
2、面试题:如何解决线程安全问题?有几种方式:两种/三种
synchronized方法、synchronized代码块、lock代码块锁
3、面试题:sleep()方法和wait()方法的异同:高频
>相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
>不同点:调用的范围/要求不同:
1、两个方法声明的位置不同:Thread类中声明sleep()方法,Object类中声明wait()方法
2、sleep()可以在任何需要的场景下调用,wait()必须使用在同步代码块或同步方法中
3、关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁
4、面试题:三种解决线程安全问题的使用顺序:
Lock(位置比较灵活) --->同步代码块(已经进入方法题,分配了相应的资源)-->同步方法(在方法体之外)
小结释放锁:
小结不会释放锁:
一:
package com.atguigu.java2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* 创建线程的方式三:实现Callable接口。---JDK 5.0新增
*
* 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程的方式更强大
* 1、call()可以有返回值的
* 2、call()可以抛出异常,被外边的操作捕获,获取异常的信息
* 3、Callable是支持泛型的
* @author gaixinyu
* @create 2021-03-25-21:04
*/
public class ThreadNew1 {
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()的返回值
Integer sum=futureTask.get();
System.out.println("总和为"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1、创建一个实现Callable的实现类
class NumThread implements Callable{
//2、实现Call方法,将此线程需要执行的操作声明在call()中
@Override
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;
}
}
二:
package com.atguigu.java2;
import java.lang.reflect.Executable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用线程池的方式创建线程
* @author gaixinyu
* @create 2021-03-25-21:30
*/
public class ThreadNew2 {
public static void main(String[] args) {
//1、提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// service.submit();//适合于Callable
//2、执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合于Runnable
service.execute(new NumberThread1());//适合于Runnable
//3、关闭连接池
service.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
if (i % 2 != 0) {
System.out.println(i);
}
}
}
}