1.程序、进程与线程
程序:为完成特定任务的静态代码
进程:执行中的程序,作为资源分配的单位
线程:是程序内部的执行路径,作为调度和执行的单位,每个线程都拥有独立的运行栈和程序计数器(PC)。
多个线程共享相同的内存单元 ->从同一个堆中分配对象,可以访问相同的变量和对象,但会有安全隐患。
2.并行与并发
3.两种实现方式
1.继承Thread类
* 1.继承Thread类
* 2.重写run方法
* 3.实例化
* 4.通过实例调用start()方法
2.实现Runnable接口(推荐)
* 1.实现Runnable接口
* 2.重写run方法
* 3.创建实现类的对象
* 4.将此对象作为参数传递
* 到Thread类的构造器中,创建Thread类的对象
4.比较创建线程的两种方式
* 开发中:优先选择:Runnable接口
* 原因:
* 1.没有单继承的局限性
* 2.共享数据
*
* Thread本身也是实现了Runnable接口
5.常用方法和优先级设置
* 测试Thread中的常用方法
* 1.start()
* 2.run()
* 3.getName()
* 4.setName()
* 5.currentThread():静态方法:返回当前执行的线程
* 6.yield():释放当前CPU执行权
* 7.join():抢夺当前执行线程的CPU执行权,当前线程阻塞,直到执行完,结束阻塞状态
* 8.stop():已过时:强制结束当前线程
* 9.sleep():让当前线程睡眠指定时间。
* 10.判断当前线程是否存活
*
* 线程的优先级:
* MAX_PRIORITY: 10
* MIN_PRIORITY: 1
* NORM_PRIORITY: 5
* 2.获取和设置线程的优先级
* getPriority()
* setPriority()
* 优先级高并不一定比优先级低的先执行,只是概率高
*/
6.线程的生命周期
7.售票案例实现
问题:线程争抢资源,导致出错
设置临界区,当一个线程对临界区进行操作时,其他线程只能在临界区外等待。
解决方式:
1.同步代码块
synchronized(static object){}
说明:
1.共享区即为需要被同步的代码 — 注意synchronized(static object){}的范围,记住一点包住的地方只有一个线程能进去,如果包到while外面,就变成单线程了,没意义了。
2.共享数据:多个线程共同操作的变量
3.同步监视器,俗称:“锁”,任何一个类的对象都可充当锁(建议利用反射机制:用类的class对象(静态)充当锁)。
要求:要求多个线程共用同一把锁
局限性:实际上是个单线程的过程
package com.atguigu;
/**
* @author : wangchuang
* @date : 2021/11/14 15:06
* @detail :
*/
class Windows implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (Windows.class) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowsTest1 {
public static void main(String[] args) {
Windows windows = new Windows();
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
2.同步方法
synchronized修饰方法
package com.atguigu;
/**
* @author : wangchuang
* @date : 2021/11/14 15:06
* @detail :
*/
class Windows implements Runnable {
private int ticket = 100;
private boolean flag = true;
@Override
public void run() {
while (true) {
if(ticket != 0) {
show();
}else{
break;
}
}
}
private synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowsTest1 {
public static void main(String[] args) {
Windows windows = new Windows();
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
8.死锁问题
解决线程安全问题的方式三:Lock锁 ----JDK5.0新增
ReentrantLock
package com.atguigu.java2;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author : wangchuang
* @date : 2021/11/14 17:19
* @detail :
*/
class Window implements Runnable {
private int ticket = 100;
//实例化锁对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//加锁
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 卖票:票号为:" + ticket);
ticket --;
}else{
break;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
t1.start();
t2.start();
t3.start();
}
}
面试题:
synchronized与Lock的异同点
同:都可以解决线程安全问题
异:同步方法的和同步代码块包括起来的临界区执行完后才会释放同步监视器,Lock需要手动的启动和释放,更加灵活。
在实际开发中使用synchronized较为多,但是更建议使用Lock,其次是同步代码块,最后才选择同步方法。
9.线程通信
题目:两个线程交替打印1 ~ 100的数
wait():当前线程阻塞,并释放同步资源监视器
notify():唤醒被wait()中优先级高的线程
notifyAll():唤醒所有wait()中的线程
说明:
1.这三个方法必须用在同步代码块或同步方法中
2.这三个方法的实际调用者必须是同步代码块或同步方法中的同步监视器。
3。这三个方法是定义在java.lang.Object包中的
面试题:
sleep()和wait()的异同?
同:都可以是当前执行线程阻塞
异:1.声明位置不同:Thread类中声明sleep(), Object类中声明wait()
2.调用要求不同:sleep()在任何需要的场景下都可以调用,wait()必须在同步代码块或同步方法下
3.关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放,wait()会释放
package com.atguigu.java3;
/**
* @author : wangchuang
* @date : 2021/11/14 18:03
* @detail :
*/
class Number implements Runnable {
private int number = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
notify();
if (number <= 100) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : " + number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommunicationThreadTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
10.生产者消费者模型
package com.atguigu.java3;
/**
* @author : wangchuang
* @date : 2021/11/14 19:19
* @detail :
*/
class Clerk{
private int productCount = 0;
//生产产品
public synchronized void produceProduct(){
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第几个产品:" + productCount);
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productCount > 0) {
System.out.println(Thread.currentThread().getName() + ":消费者开始消费第几个产品:" +productCount);
productCount--;
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producers extends Thread{
private Clerk clerk;
public Producers(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ":开始生产产品......");
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumers extends Thread{
private Clerk clerk;
public Consumers(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + "开始消费产品......");
while (true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producers p1 = new Producers(clerk);
p1.setName("生产者1");
Consumers c1 = new Consumers(clerk);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
11.Callable接口
如何理解实现Callable接口的方式创建多线程比实现Runnable接口更强大?
1.call() 可以有返回值
2.call() 可以抛出异常
3.Callable 支持泛型
package com.atguigu.java4;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author : wangchuang
* @date : 2021/11/14 20:30
* @detail :
*
*/
class NumThread implements Callable {
@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;
}
}
public class ThreadNew {
public static void main(String[] args) {
NumThread numThread = new NumThread();
FutureTask futureTask = new FutureTask(numThread);
Thread thread = new Thread(futureTask);
thread.start();
try {
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
12.线程池
package com.atguigu.java5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author : wangchuang
* @date : 2021/11/14 20:59
* @detail :
*/
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);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//设置线程池的属性
ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
// service1.setCorePoolSize(20);
// service1.setKeepAliveTime(1, 10000);
//2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口的实现类的对象
service.execute(new NumberThread()); //适用Runnable
service.submit(new NumberThread()); //适用Callable
//3.关闭线程池
service.shutdown();
}
}