注意事项!
synchronized(几把特殊的锁){}
- 字符串常量:
""
- Runnable中的对象:
this
- 字节码对象:
MyRunnable.class
在当前线程下,如果有子线程正在执行,想等待子线程执行完了再继续执行,可以使用Thread下的join方法(
子线程对象.join()
)
不同步,不安全,效率高(需要则用锁机制) | 同步,安全,效率很低(很少使用) |
---|---|
ArrayList | Vector |
HashMap | Hashtable(不允许null键值) |
StringBuilder | StringBuffer |
使用锁的时候最好使用try...catch...finally{ lock.unlock(); }
语句,这样可以最大限度避免死锁
try {} finally { lock.unlock(); }
这种结构也是允许的!
包装类不适合当做锁,如果包装类的值发生改变,会自动装箱,这时可能会new一个新的对象,这个锁就毫无意义了
一、线程不安全
线程安全:如果有多个线程在同时运行,这些线程可能会同时运行这段带啊,如果每次运行结果是一样的,其他变量的值也和预期是一样的,那线程就是安全的。
如果要保存线程安全地加一把锁!
线程安全问题:
- ①线程安全是由静态变量以及全局变量(公共变量)引起的;
- ②多线程对公共变量进行读操作是线程安全的。
- ③多线程对公共变量进行写操作,得考虑线程同步问题,否则线程就不安全。
二、线程同步
同步机制synchronized /'sɪŋ krə naɪ zd/
为了保证每个线程都能正常执行原子操作,java引入了线程同步机制
- ①同步代码块。
- ②同步方法。
- ③锁机制。
三、同步代码块
-
同步代码块:
synchronized
关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
适合相同逻辑代码(代码一样)进行同步。
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
- 锁对象,可以是任意类型。
- 多个线程对象 要使用同一把锁
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只有等待(BLOCKED)。
class Ticket implements Runnable{
private int ticket = 10;
// new 一个公共锁,谁拿到锁,谁就执行,执行完了,就释放锁。
Object lock = new Object();
public void run() {
String threadName = Thread.currentThread().getName();
while (ticket > 0){
try {
System.out.println("正在购票中,请稍后...");
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 同步代码块
synchronized(lock) {
if(this.ticket > 0) {
ticket --;
System.out.println(threadName + "受理成功,已成功购票,余票: " + this.ticket);
} else {
System.out.println(threadName + "受理失败,余票: " + this.ticket);
}
System.out.println();
}
}
}
}
/****************************************************/
public class Main {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket, "Window 1: ");
Thread t2 = new Thread(ticket, "Window 2: ");
Thread t3 = new Thread(ticket, "Window 3: ");
t1.start();
t2.start();
t3.start();
}
}
四、同步方法
同步方法:使用synchronized修饰的方法,线程A执行该方法的时候,其他线程只有等待。。
同步方法没有同步代码块灵活!
格式:
public synchronized void method(){
可能会发生线程安全问题的代码。
}
同步方法的同步锁对象:
- 对于非static,同步锁就是this对象。;
- 对于static,同步锁是当前方法所在类的字节码对象( 类名.class,类模板)
五、Lock锁
包位置:java.util.concurrent.locks.Lock
比synchronized代码块和方法更流弊,更强大,更面向对象
加锁与释放锁的方法:(省略public)
-
void lock()
: 加同步锁(获取同步锁); -
void unlock()
: 释放同步锁;
代码示例,打印进度条:reentrant
/riː'en trən t(不发音)/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadLock implements Runnable{
private int publicVariable = 50;
Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
Random random = new Random();
System.out.println("----别慌,先让我执行完! --> " + Thread.currentThread().getName());
// 模拟一个进度条耍耍
for (int i = 1; i <= publicVariable; i++) {
System.out.print("[");
int j = 0;
while (j++ < i) System.out.print(">");
while (j++ < publicVariable) System.out.print("-");
System.out.print("]" + (i * 2) + "%");
try { Thread.sleep(random.nextInt(100) + 1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.print("\r");
}
System.out.println("----OK,我执行完了\n");
lock.unlock();
}
}
/*********************************/
public class Test {
public static void main(String[] args) throws Exception {
ThreadLock tl = new ThreadLock();
Thread t1 = new Thread(tl, "线程 NO.1 ");
Thread t2 = new Thread(tl, "线程 NO.2 ");
Thread t3 = new Thread(tl, "线程 NO.3 ");
t1.start();
t2.start();
t3.start();
}
}
如果想使用Lock锁来使用obj.wait和obj.notify功能,如下:
Condition c = lock.newCondition();
c.await() :相当于wait
c.signal() :相当于notify