多次启动一个线程是非法的
静态是线程安全的。 但不是绝对的线程安全。
使用Runnable 接口的形式
class runDate(){
public void run(){
}
}
class ddd(){
psvm(){
runData r= new runData();
Thread t=new Thread(r);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t.start();
t1.start();
t2.start();
}
}
这种形式虽然创建了多了线程。 但是因为传入的r是同一个对象。 随意这样四个线程的t是线程共享的。 可以实现4个线程卖100个票的例子。
而继承Thread类形式。因为每创建一个线程,就要创建一个本身的示例。 所以每个线程运行的线程任务实例 都是单独的。所以会出现卖了400张票的问题
线程安全问题的现象
线程安全问题产生的原因:
- 多个线程在操作共享的数据包。
- 操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
解决思路:
就是将多条操作共享数据的线程代码封装起来。当有线程在执行这些代码的时候,
其他线程是不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
同步代码块
在java中用同步代码块就可以解决这个问题。
同步代码块的格式
synchronized(对象){
需要被同步的代码;
}
同步的好处: 解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。
同步的前提
同步的前提:必须有多个线程并使用同一个锁
同步函数
public synchronized void add(int num){
}
同步函数的锁是this 即当前对象。
同步函数和同步代码块的区别:
1.同步函数的锁是固定的this。
2.同步代码块的锁是任意的对象。也可以是this.
建议使用同步代码块。
静态同步代码块的锁
静态的同步函数时没有this的。
静态的同步函数使用的锁是 该函数所属字节码文件对象。 可以用 getClass方法获取。
也可以用当前类名.class 表示
单例模式涉及到多线程问题
实际代码中,尽量使用饿汉式。
面试时可能会问懒汉式
同步判断懒汉式单例
死锁示例
class Test implements Runnable {
private boolean flag = true;
Test(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.LOCK_A) {
System.out.println("if LOCK_A,,,");
synchronized (MyLock.LOCK_B) {
System.out.println("if LOCK_B,,,");
}
}
} else {
synchronized (MyLock.LOCK_B) {
System.out.println("else LOCK_B,,,");
synchronized (MyLock.LOCK_A) {
System.out.println("else LOCK_A,,,");
}
}
}
}
}
class MyLock {
static final Object LOCK_A = new Object();
static final Object LOCK_B = new Object();
}
class DeadLockTest {
public static void main(String[] args) {
Test a = new Test(true);
Test b = new Test(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}