1.同步的安全问题
1.同步代码块
格式:
synchronized(锁对象){
//同步代码
}
锁对象:它必须是一个引用类型,可以是任何对象。另外:
要保证多个线程要共享“同一个锁对象”。
示例:
1).Tickets类:
public class Tickets implements Runnable {
private int tickets = 100;
private ArrayList list = new ArrayList();
@Override
public void run() {
while (true){
//使用同步代码块--让无序的线程到这里排队
synchronized (list) {//当一个线程进入,加锁,其它线程会自动在外面等待
//判断是否有票
if (this.tickets > 0) {
int t = tickets;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(Thread.currentThread().getName() + " 抢走一张票:" + t);
} else {
System.out.println("没票了,结束....");
break;
}
}//当一个线程执行完毕后,会自动释放锁
}
}
}
2).测试类:
public class Demo {
public static void main(String[] args) {
//1.创建一个票对象
Tickets tickets = new Tickets();
//2.创建两个线程,模拟两个窗口
Thread t1 = new Thread(tickets);
Thread t2 = new Thread(tickets);
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}
2.同步方法[常用]
格式:
public synchronized int getTicket(){
//同步代码
}
示例:
Tickets类:
public class Tickets implements Runnable {
private int tickets = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true){
int t = getTicket();
if (t > 0) {
System.out.println(Thread.currentThread().getName() +
" 获取一张票:" + t);
}else{
System.out.println("没票了");
break;
}
}
}
//同步方法可以有"锁对象"--this
public synchronized int getTicket(){
//判断是否有票
if (this.tickets > 0) {
int t = tickets;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
return t;
} else {
return 0;
}
}
//扩展:
//1.静态方法是否可以被声明为:同步方法?--可以
//静态方法中不能使用:this,和super
//它的默认"锁对象"是本类的Class对象【在反射讲】
public synchronized static void show(){
}
//2.静态方法中是否可以定义同步代码块?--可以
public static void show2(){
synchronized (obj){//静态的成员变量做锁
}
}
}
3.Lock锁
- 锁的简介
当多个线程同时操作数据时,会导致数据的冲突。
例如:存钱--同时用微信和支付宝存钱,发生流程的冲突时,执行流程为:
1.微信读取账户金额
2.支付宝读取账户金额
3.微信存入=读取的金额+存入的金额
4.支付宝存入=读取的金额+存入的金额
5.最终金额=支付宝存入的金额(发生覆盖)
- 应用场景
如果我们某一个线程在使用完之前,其他线程不能对其修改,就需要对这个线程增加一个线程锁。
- demo
格式:
Lock l = new 子类对象();
l.lock(); //加锁
try {
// 同步代码
} finally {
l.unlock(); //解锁
}
Tickets类:
public class Tickets implements Runnable {
private int tickets = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();//加锁
try {
//判断是否有票
if (this.tickets > 0) {
int t = tickets;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(Thread.currentThread().getName() +
" 抢走一张票:" + t);
} else {
System.out.println("没票了,结束....");
break;
}
}finally {
lock.unlock();//解锁
}
}
}
}
4.死锁
进程A中包含资源A,进程B中包含资源B,A的下一步需要资源B,B的下一步需要资源A,所以它们就互相等待对方占有的资源释放,所以也就产生了一个循环等待死锁。
2.Thread
1.Thread构造方法
public Thread():分配一个新的线程对象
public Thread(String name):分配一个指定名字的新的线程对象
任何一个线程都有一个默认的线程名:Thread-索引值
可以直接通过Thread的 getName()获取线程名
public Thread(Runnable target):分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字.
2.Thread成员方法
1.public String getName():获取当前线程的名称
public void setName():设置线程名称
2.public void start():导致此线程开始执行:java虚拟机调用此线程的run方法
3.public void run():此线程要执行的任务在此处定义代码.
4.public void void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂定
5.public static Thread currentThread():返回对当前正在执行的线程对象的引用
3.等待唤醒案例
public class Demo {
private static Object obj = new Object();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
synchronized (obj) {
for (int i = 0; i < 20; i++) {
System.out.println("i = " + i);
if(i == 10){
try {
System.out.println("开始无限等待...");
obj.wait();
System.out.println("被唤醒了,恢复运行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}.start();
//让主线程暂停1秒,目的:让上面的线程先进入到等待状态
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//定义另一个线程,唤醒所有在obj锁上等待的其它线程
new Thread(){
@Override
public void run() {
System.out.println("唤醒线程开始执行...");
synchronized (obj){
//唤醒等待线程
obj.notifyAll();
}
}
}.start();
}
}