一.线程安全 二.解决线程安全 1.使用同步代码块 2.使用同步方法 3.静态方法 4.使用Lock锁
一.线程安全
RunnableImpl run = new RunnableImpl();
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
当创建多个线程操作同一个 实现类时,由于数据是共享的 ,一个线程可能会 执行run方法时被其他线程抢到,权限
第一个线程还没有操作完 ,而第二个线程又到来了 操作这个数据 就可能发生混乱
二.解决线程安全
1.使用同步代码块
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象作用: 把同步代码块锁住,只让一个线程在同步代码块中执行
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
public class RunnableImpl implements Runnable{
private int ticket = 100;
Object obj = new Object(); //创建一个锁对象 可以是随意的 同步锁
@Override
public void run() {
while(true){
synchronized (obj){ //同步代码块
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
原理: 当t1线程抢到执行权时,会检查synchronized 代码块 是否有 锁对象,发现有就会获取到锁对象,进入到同步中执行.
这时当t2抢到执行权时,遇到synchronized 代码块会继续检查,发现没有就会阻塞,直到t1执行完锁对象才被归还,t2才能又获取 锁对象进入代码块执行. 一句话: 同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁对象进不去代码块
缺点: 频繁的判断锁,释放锁获取锁,效率降低
2.使用同步方法
public class Syn implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
pay();
}
}
public synchronized void pay() {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + num + "张票");
num--;
}
}
Syn syn = new Syn();
System.out.println(syn);
Thread t1 = new Thread(syn);
Thread t2 = new Thread(syn);
Thread t3 = new Thread(syn);
t1.start();
t2.start();
t3.start();
原理就是锁对象就是 this 实现Runnable类的对象 syn
所以第一种方法不用创建一个对象同步代码块 传入this也可以
3.静态方法
public class Syn implements Runnable {
public static void payTicketStatic(){
synchronized (Syn.class){ 静态方法不能使用this 本类的class文件对象也可以
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}}}
4.使用Lock锁
1.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
2.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
public class LockDemo implements Runnable {
private int num = 100;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true){
l.lock();
if(num > 0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"--正在卖第"+num+"票");
num--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
System.out.println(l);}}}
LockDemo lockDemo = new LockDemo();
Thread t= new Thread(lockDemo);
Thread t1= new Thread(lockDemo);
Thread t2= new Thread(lockDemo);
t.start();
t1.start();
t2.start();
线程优先级
1.每个线程的优先级默认是5 最高可以设置为10最低1 2.t.setPriority( 10) 这样也不是全执行它,只是执行几率更大
jion
1.设置join 后会等待这个线程执行完,才执行其他的线程 与Python 类似 在start() 之后设置 t.join( )
yield
1.在run方法内部,设置 Thread.yield() ; 作用为 让出cpu执行权 ,进入就绪状态,还可以抢夺资源
守护线程
1. t.setDaemon(true); 在start之前设置 作用为 这个线程随着出线程的结束而结束
线程死锁
1.当有两个线程,他们都是同步的, 当t1 拿到一把锁lock1 ,t2拿到一把锁lock2 这时t1还想拿锁lock2 t2还想拿锁lock1
这样它们就会僵住,称为线程死锁.