Java线程(下)
2023/6/10大约 7 分钟
1. 线程同步
1.1线程安全问题:
- 同步和异步:CPU的切换
- 临界区
- 阻塞和非阻塞:限制访问临界区权限
线程安全:
- 多个线程同时操作同一个临界区共享资源的时候,可能会出现的业务安全问题
- 银行取钱
package 线程下; public class Demo1 { public static void main(String[] args) { Manger manger=new Manger(); Thread t1=new Thread(manger); Thread t2=new Thread(manger); t1.start(); t2.start(); } } class Manger implements Runnable{ Account account=new Account(); @Override public void run() { CheckBlance(60000); } public void CheckBlance(int money){ if (account.getBalance()>money){//出现线程安全问题 System.out.println("余额充足"); account.setBalance(account.getBalance()-money); System.out.println("余额剩余:"+account.getBalance()); }else { System.out.println("余额不足:"+account.getBalance()); } } } class Account{ private String id; private float balance=100000; public String getId() { return id; } public void setId(String id) { this.id = id; } public float getBalance() { return balance; } public void setBalance(float balance) { this.balance = balance; } @Override public String toString() { return "Account{" + "id='" + id + '\'' + ", balance=" + balance + '}'; } }
- 银行取钱
1.2 线程同步的实现
同步方法:
//在方法上加上synchronized关键字
package 线程下;
public class Demo1 {
public static void main(String[] args) {
Manger manger=new Manger();
Thread t1=new Thread(manger);
Thread t2=new Thread(manger);
t1.start();
t2.start();
}
}
class Manger implements Runnable{
Account account=new Account();
@Override
public void run() {
CheckBlance(60000);
}
public synchronized void CheckBlance(int money){
if (account.getBalance()>money){
System.out.println("余额充足");
account.setBalance(account.getBalance()-money);
System.out.println("余额剩余:"+account.getBalance());
}else {
System.out.println("余额不足:"+account.getBalance());
}
}
}
class Account{
private String id;
private float balance=100000;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public float getBalance() {
return balance;
}
public void setBalance(float balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id='" + id + '\'' +
", balance=" + balance +
'}';
}
}同步块:
public class Test {
public static void main(String[] args) {
synchronized (Test.class) {//同步代码块
int a = 1;
}
}
}
public class Test{
private String name = "xiaoming";
private String id = "0753";
public void setName(String name) {
synchornized(name) {//同步代码块
this.name = name;
}
}
public void setId(String id) {
synchornized(id) {
this.id = id;
}
}
}小结:
- 同步:通过加锁让出现线程安全问题的核心代码能够实现同步,推荐使用同步块,锁定范围更加精准
1.3 锁的概念:
- 要实现同步就需要锁,一把钥匙对应一把锁,当我们实现同步的时候就只能是一把钥匙和一把锁
锁的分类:
- 类锁:类的Class对象
- 对象锁:基于当前对象this
- 私有锁:我们创建的某个对象
以上三种锁本质上都是对象锁
对个线程之间使用同一个锁,使用同一个对象锁类锁:
public synchronized static void test{
System.out.println("这是第一种类锁");
}
public static void test2(){
synchronized (Demo2.class){
System.out.println("这是第二种类锁");
}
}对象锁:
public synchronized void test3(){
System.out.println("这是对象锁");//谁调用这个方法的对象谁就使用这个锁
}
public void test4(){
synchronized (this){
System.out.println("这是对象锁");
}
}私有锁:
String lock=new String();
public void test5(){
synchronized (lock){
System.out.println("这是私有锁");
}
}1.4 同步案例:
sleep()
- 让出CPU
- 不让出锁
1.5 死锁
原理图:

1.6同步锁Lock
- JDK5之后
- Lock是一个接口,由ReentrantLock构建锁对象 synchronized和Lock的区别:
- Lock是一个接口,syn是一个关键字
- Lock通过方法调用,很清晰的实现了加锁和释放锁,syn自动,核心代码执行完后,自动释放锁
- Lock可以让等待锁的线程响应中断,syn不行,等待的线程要一直等待下去
- Lock的性能高于syn
2.线程通信
2.1什么是线程通信:
- 线程与线程之间相互独立,但是可以实现资源共享
- 线程通信互相发送数据
- 实现线程通信的方法来自Object
- 通过共享数据的方式实现线程间的通信
| 方法 | 方法说明 |
|---|---|
| void wait() | 当前线程等待并释放所占有的锁,直到另一个线程用notify()或者notifyAll()方法 |
| void notify() | 唤醒当前正在等待的单个线程 |
| void notifyAll() | 唤醒当前正在等待的所有线程 |
- notifyAll其实和notify一样,也是用于唤醒,但是前者是唤醒所有调用
wait()后处于等待的线程,而后者是看运气随机选择一个
2.2 生产者消费者模式:
- 也就是给出生产者和消费者(来自操作系统里面的概念),然后一个生产一个消费,或多个生产多个消费
package 线程下; public class Demo5 { public static void main(String[] args) { Pool pool=new Pool(); Producer producer = new Producer(pool); Consumer consumer = new Consumer(pool); Consumer consumer1=new Consumer(pool); producer.start(); consumer.start(); consumer1.start(); } } //面包 class Bread { private int num; } //共享资源池 class Pool{ int index=0; public synchronized void push(){ while (this.index>20){ try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } this.index++; this.notifyAll(); } public synchronized void pop(){ while (this.index==0){ try { this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } index--; notify(); } } class Producer extends Thread{ Pool pool; public Producer(Pool pool){ this.pool=pool; } @Override public void run() { while (true){ pool.push(); System.out.println(Thread.currentThread().getName()+"生产者:"+pool.index); } } } class Consumer extends Thread{ Pool pool; public Consumer(Pool pool){ this.pool=pool; } @Override public void run() { while (true){ pool.pop(); System.out.println(Thread.currentThread().getName()+"消费者:"+pool.index); } } }
3. 线程池:
3.1线程池概念:
- 可复用的线程技术
- 程序中有可能涉及大量生存期很短的线程
- 多次重复过程:创建新的线程对象,对象销毁
- 使用线程池:
- 存储多个线程对象的资源池
- 当需要新的线程对象时,从线程池中去取,将线程对象放回资源池
- 提高启动多个线程的性能
3.2 JDK内置线程池:
JDK5开始提供
线程池分类:
- SingleThreadPool:单线程化线程池,资源池中只有唯一的一个工作线程来执行任务,任务按照指定顺序执行(FIFO,优先级别)
- FixedThreadPool:一个可重用的固定数量的线程池
- CachedThreadPool:一个可重用的数量可变的线程池,可根据需要创建新线程
- ScheduledThreadPool:一个固定数量的线程池,支持定时及周期任务的调度
线程池API:
使用Executors实现线程池:

相当于一个工具类,底层也是基于ThreadPoolExecutor,在大型并发系统中不推荐使用Executors,系统风险
线程池的简单使用
package 线程下;
import java.util.concurrent.*;
public class Demo4 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(3);
Task t1 = new Task();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,6,5,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
// threadPoolExecutor.execute(t1);
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":线程任务执行");
}
}
4. 线程调度
4.1调度线程池:
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo4 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(3);
Task t1 = new Task();
pool.scheduleWithFixedDelay(t1,5,5, TimeUnit.SECONDS);
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":线程任务执行");
}
}4.2 Timer和TimerTask
- Timer是定时器:定时执行任务
- TimerTask任务对象
他们是单线程任务调度
- 任务放入任务列表当中,Timer对象接收任务和时间的绑定
- TimerThread在Timer对象创建的时候成为一个守护线程
- TimerThread找到任务列表中最近要执行的任务,记录后休眠
- 到了任务执行时间,TimerThread执行任务
- 再次遍历列表,记录下一次执行的任务和时间
同一时间只有一个任务被调度,上一个执行的会影响下一个执行的时间
import java.util.Timer;
import java.util.TimerTask;
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
long delay=1000;
long period=1000;
MyTimerTask myTimerTask = new MyTimerTask();
Timer timer=new Timer();
timer.schedule(myTimerTask,1000,1000);
Thread.sleep(10000);
timer.cancel();
}
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"定时器的任务");
}
}