如果是非线程安全,多个线程对统一个对象中的实例变量进行并发的访问时发生。产生的后果就是出现脏读。
非线程安全问题存在与实例变量中。如果是方法内部的私有变量,则不存在非线程安全问题。因为方法内部的变量是私有的。
线程的start和run方法的区别?
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
attack();
}
};
t.run();
t.start();
}
public static void attack() {
System.out.println("Go");
System.out.println("currentThread: " + Thread.currentThread().getName());
}
// Go
// currentThread: main
// Go
// currentThread: Thread-0
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
// 调用本地方法,用JVM_StartThread创建一个子线程(Thread_entry)由子线程调用run方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
- 调用start()方法,会创建一个新的子线程并启动
- run()方法只是Thread的一个普通方法的调用
Thread方法
方法名 | 含义 |
---|---|
currentThread() | 返回代码段正在别那个线程调用 |
isAlive() | 判断线程是否处于活动状态。活动状态就是线程已经启动还没终止的。线程处于正在运行或者准备开始运行的状态,就认为是活动状态。 |
sleep() | 在指定的毫秒数让当前线程休眠 |
getId() | 取得线程的唯一标识 |
yield() | 放弃当前CPU的资源,将他让给其他任务去占用CPU的执行时间。但放弃的时间是不确定的。有可能刚刚放弃,马上又获得CPU时间片,不会影响锁的行为 |
setPriority() | 设置线程的优先级别 |
setDaemon() | 设置称为守护线程 |
join() | 使所属线程thread对象正常执行完run方法中的任务,而使当前线程无限阻塞,等待thread线程销毁之后在继续执行线程z后面的代码。 |
线程的优先级是具有继承性的,比如A线程启动B线程,则B线程的优先级与A是一样的。
join内部使用的wait方法进行等待。而sychronized关键字使用的是,对象监视器。原理做为同步的。
如果join过程中,当前线程对象对象被中断,则当前线程出现异常。
Join 与 Sleep的区别
方法 | 区别 |
---|---|
join(Long) | join(long)内部是使用wait(long)实现的,所以join(long)方法具有释放锁的特点。 |
Sleep(long) | 该方法不会释放锁。 |
Sleep和Wait的区别?
- wait是Object类中的方法,Sleep是Thread类中的方法
- sleep()方法可以在任何地方使用
- wait()方法只能在synchronized方法或者在synchronized块中使用(因为wait只有获得锁之后才会释放)
最主要的区别:
- Thread.sleep()只会让出CPU,不会导致锁行为的改变。
- Object.wait()不仅让出CPU,还会释放已经占有的同步资源锁。
notify和notifyAll的区别?
锁池:假设线程A已经拥有了某个对象(不是类)的锁,而其他线程B、C想要调用这个对象的某个synchroized方法(或者块),由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞、进入一个地方去等待锁的释放,这个地方便是对象的锁池。
- notify会从锁池中随机选择一个唤醒
- notifyAll会唤醒锁池中所有的线程
wait与notify
wait和notify都是object中的方法
方法 | 方法名 |
---|---|
wait() | wait方法用来将当前线程放入阻塞队列中,并且在wait所在的代码行停止执行,直到接到通知或者被中断为之,在调用wait方法之前,线程必须获得该对象的对象级别的锁,即只能在同步方法,或者同步代码块中调用wait方法,在调用wait方法之后,当前线程释放锁。在wait返回之前,线程和其他线程竞争重新获得锁。如果调用wait时没有持有适当的锁,则抛出illegalMonitorStateException |
notify() | 也要在同步方法或者同步方法块中调用,线程必须获得该对象的锁,如果调用notify方法的对象没有持有适当的锁,则会抛出异常。该方法用来通知那些可能等待该对象锁的其他线程,如果有多个线程等待,则有线程规划器随机挑选一个程先wait的线程,对其发出通知notify,并使它等待获取该对象的对象锁。在执行notify之后,当前线程不会马上释放对象锁,程先wait的线程也不能马上获取该对象锁。要等到执行notify方法的线程将程序执行完成之后,当前线程才会释放锁。而程先wait状态的线程才可以获得该对象锁。 |
wait(long) :
带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
简单的总结就是:
wait使当前线程处于等待状态,notify使等待的线程继续运行。
每个锁对象都有两个队列,一个是就绪队列一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。
一个线程被唤醒后,才会进入就绪队列,等待CPU的调度,反之,一个线程被wait后,就会进入阻塞队列,等待下一次的唤醒。
当线程程wait状态的时候,调用线程对象的Interrupt方法会出现InterruptExcepthion异常。
方法notify只能随机唤醒一个线程。
当多次调用notify方法时,会随机将等待的线程进行唤醒
总结:
- 在执行同步代码块就会释放对象的锁。
- 在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。
- 在执行同步代码块的过程中,执行了锁所属对象的wait方法,这个线程就会释放对象锁。而此线程对象就会进入线程等待池中,等待被唤醒。
Thread和Runnable的区别?
- Thread是实现了Runnable接口的类,使得run支持多线程
- 因类的单一继承原则,推荐使用Runnable接口
如何给run方法传递参数?
如何处理线程返回值?
-
主线程等待
public class CycleWait implements Runnable { private String value; public static void main(String[] args) throws InterruptedException { CycleWait cw = new CycleWait(); Thread t = new Thread(cw); t.start(); while (cw.value == null) { Thread.sleep(100); } System.out.println(cw.value)</