多线程
线程同步
同步代码块
- 同步代码块中锁的对象可以是任意对象,但必须是唯一的,用static修饰
静态方法中的同步代码块锁的是类对象:People.class
售票操作
class Worker extends Thread{
private static int ticket = 100;
public static Object obj = new Object();
@Override
public void run() {
show();
}
public void show(){
synchronized (obj){
while (true){
if (ticket<=0){
System.out.println("票已售罄");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这是第"+ticket--+"张票");
}
}
}
}
测试
public class ShowTicket {
public static void main(String[] args) {
Worker worker01 = new Worker();
Worker worker02 = new Worker();
Worker worker03 = new Worker();
Worker worker04 = new Worker();
worker01.start();
worker02.start();
worker03.start();
worker04.start();
}
}
同步方法
非静态同步方法————锁住单个对象
- 锁的对象是:当前实例对象。
若线程A进入非静态同步方法,进入后休眠1秒,此时释放CPU执行权,但不释放锁。
其他同一对象的线程虽然拿到了执行权,但是无法进入同步方法进行执行。
public class synchronizedMethod {
public static void main(String[] args) {
HappyNewYear thread = new HappyNewYear();
// 两个线程中传入同一个线程对象
Thread thread1 = new Thread(thread);
Thread thread2 = new Thread(thread);
thread1.start();
thread2.start();
}
}
class HappyNewYear implements Runnable{
@Override
public synchronized void run() {
System.out.println("HAPPY");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("NEW");
System.out.println("YEAR");
}
}
输出:
HAPPY NEW YEAR
HAPPY NEW YEAR
总结:两个线程中传入同一个线程对象,不会出现线程安全问题
若传入该类的其他对象,会导致线程不安全——————出错
静态同步方法————锁住该类(类中所有对象)
- 锁的对象时:类型.class
若线程A进入静态同步方法,进入后休眠1秒,此时锁住了这个方法所在类,此时释放CPU执行权,但不释放锁。
其他对象的线程虽然拿到了执行权,但是无法进入该类中执行方法,需要等待获取类的锁
class HappyNewYear02 implements Runnable{
@Override
public synchronized void run() {
method();
}
public static synchronized void method(){
System.out.print("我");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(" 是");
System.out.println(" 小学生");
}
}
测试
public class synchronizedMethod02 {
public static void main(String[] args) {
HappyNewYear02 thread = new HappyNewYear02();
HappyNewYear02 thread02 = new HappyNewYear02();
// 两个线程中传入不同的对象
Thread thread1 = new Thread(thread);
Thread thread2 = new Thread(thread02);
thread1.start();
thread2.start();
}
}
总结:两个线程中传入可传入一个类的不同对象,也不会出现线程安全问题
Lock锁
- 显式锁,需要手动开启和关闭,且仅为代码块锁
class Lock implements Runnable{
int num = 10;
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
if (num<=0){
break;
}
lock.lock();
try{
System.out.println(num--);
}finally {
lock.unlock();
}
}
}
}
测试
public class LockDemo {
public static void main(String[] args) {
Lock lock = new Lock();
new Thread(lock).start();
new Thread(lock).start();
}
}
wait ( ) 与notify ( )
- sleep(long)————不释放锁
- wait()————释放锁,搭配notifyAll(),在同步锁中使用
- 其中线程1先获得A锁,然后调用wait()进入阻塞状态,释放A锁
- 线程2获得了B锁,然后获得了A锁(可重入锁原理),最后唤醒线程1的阻塞状态
- 线程1最后获得了B锁
public class WaitDemo {
public static void main(String[] args) {
new Thread(new LockA()).start();
new Thread(new LockB()).start();
}
}
class LockA implements Runnable{
@Override
public void run() {
synchronized ("A"){
System.out.println("线程1获得了A锁");
try {
"A".wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized ("B"){
System.out.println("线程1获得了A和B锁");
}
}
}
class LockB implements Runnable{
@Override
public void run() {
synchronized ("B"){
System.out.println("线程2获得了B锁");
synchronized ("A"){
System.out.println("线程2获得了B和A锁");
"A".notifyAll();
}
}
}
}
输出:
线程1获得了A锁
线程2获得了B锁
线程2获得了B和A锁
线程1获得了A和B锁
单例模式
懒汉单例————线程不安全
- 当多个线程同时进入getInstance()方法时,会创建多个实例
public class LazyDemo {
public static void main(String[] args) {
// 多线程,同时进入getInstance方法
new Thread(new Factory02()).start();
new Thread(new Factory02()).start();
new Thread(new Factory02()).start();
}
}
class Lazy{
private Lazy(){}
private static Lazy man = null;
public static Lazy getInstance(){
if (man==null){
man = new Lazy();
}
return man;
}
}
class Factory02 implements Runnable{
@Override
public void run() {
Lazy instance = Lazy.getInstance();
System.out.println(instance.hashCode());
}
}
输出:
258124931
237601000
595124058
懒汉单例————线程安全
- 双重检查
class Single{
private Single(){}
private static volatile Single man;
public static Single getInstance(){
if (man==null){
synchronized (Single.class){
if (man==null){
man = new Single();
}
}
}
return man;
}
}
class Factory03 implements Runnable{
@Override
public void run() {
Single instance = Single.getInstance();
System.out.println(instance.hashCode());
}
}