同步锁
同步与异步
同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。
坏处就是效率会降低,不过保证了安全。
异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。
坏处就是有安全隐患,效率要高一些。
synchronized关键字
语法格式:
synchronized (锁对象){
需要同步的代码(也就是可能出现问题的操作共享数据的多条语句);
}
前提
- 同步需要两个或者两个以上的线程(单线程无需考虑多线程安全问题)
- 多个线程间必须使用同一个锁
特点
synchronized同步关键字可以用来修饰方法,称为同步方法,使用的锁对象是this
synchronized同步关键字可以用来修饰代码块,称为同步代码块,使用的锁对象可以任意
同步的缺点是会降低程序的执行效率,但我们为了保证线程的安全,有些性能是必须要牺牲的
为了性能,加锁的范围需要控制好
售票案例
Runnable
package cn.tedu.tickets;
/**
* 解决接口方式售票安全隐患问题
*/
public class TicketRunnableV2 {
public static void main(String[] args) {
TicketRV2 t = new TicketRV2();//统一的目标业务对象
Thread m1 = new Thread(t,"马钊");//多线程对象
Thread m2 = new Thread(t,"雨来");//多线程对象
Thread m3 = new Thread(t,"泡泡");//多线程对象
Thread m4 = new Thread(t,"赵彪");//多线程对象
m1.start();
m2.start();
m3.start();
m4.start();
}
}
class TicketRV2 implements Runnable{
int ticket = 100;
//定义一个唯一的锁对象
Object o =new Object();
@Override
public void run() {
while (true) {
/*
synchronized (new Object())
锁不住,会new多个Object()对象
需要定义一个同步代码块,锁对象需要唯一
*/
synchronized (o) {
if (ticket > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
} else break;
}
}
}
}
Thread
package cn.tedu.tickets;
/**
* 解决继承Thread方式售票安全隐患问题
*/
public class TicketThreadV2 {
public static void main(String[] args) {
TicketTV2 t1 = new TicketTV2("1号窗口马钊");
TicketTV2 t2 = new TicketTV2("2号窗口桂宏宇");
TicketTV2 t3 = new TicketTV2("3号窗口雨来");
TicketTV2 t4 = new TicketTV2("4号窗口泡泡");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketTV2 extends Thread{
static int ticket = 100;
//Object o = new Object();
public TicketTV2(String name) {
super(name);
}
@Override
public void run() {
while (true){
synchronized (TicketTV2.class){
if (ticket >0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "=" + ticket--);
} else break;
}
}
}
}
StringBuffer与StringBuilder
StringBuffer
加了synchronized ,性能相对较低(要排队,同步),安全性高
StringBuilder
去掉了synchronized,性能更高(不排队,异步),存在安全隐患
线程创建其他方式
ExecutorService/Executors
ExecutorService:
用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理
execute(Runnable任务对象)
把任务丢到线程池
Executors
辅助创建线程池的工具类
newFixedThreadPool(int nThreads)
最多n个线程的线程池newCachedThreadPool()
足够多的线程,使任务不必等待newSingleThreadExecutor()
只有一个线程的线程池
示例
/*
创建线程池
Executors
newFixedThreadPool(线程数)方法创建指定线程数的线程池
线程池类型为:ExecutorService
*/
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0;i < 5;i++){
//使用池对象完成任务,人任务参数为target
pool.execute(target);
}