1. 多线程访问临界资源
1.1 临界资源
多个线程同时访问的资源
1.2 解决临界资源问题(如:卖票案例)
一个线程访问临界资源时,给这个资源“上一把锁”,这时候其他线程就得在外面等待
1.3 锁
任意对象都可以被当做锁来使用
1.4 同步代码块
Synchronized:同步 有等待
Asynchronized:异步 没有等待,各执行各的
class Ticket implements Runnable{
private int ticket=1000;
private Object object=new Object();
@Override
public void run() {
while (true){
synchronized (object) {
if(ticket<1){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
ticket--;
}
}
}
}
class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread w1=new Thread(ticket,"w1");
Thread w2=new Thread(ticket,"w2");
Thread w3=new Thread(ticket,"w3");
w1.start();
w2.start();
w3.start();
}
}
1.5 同步方法
class Ticket implements Runnable{
private static int ticket=1000;
@Override
public void run() {
while (true){
if(!sellTicket()){
break;
}
}
}
//静态方法的锁是Ticket.class 非静态方法的锁是this
public static synchronized boolean sellTicket(){
if(ticket<1){
return false;
}
System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
ticket--;
return true;
}
}
class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread w1=new Thread(ticket,"w1");
Thread w2=new Thread(ticket,"w2");
Thread w3=new Thread(ticket,"w3");
w1.start();
w2.start();
w3.start();
}
}
1.6 ReentrantLock类(可重入锁)
jdk1.5之后加入新的接口Lock,ReentrantLock是Lock接口的实现类
class Ticket implements Runnable{
private int ticket=1000;
ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try {
if(ticket<1){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket);
ticket--;
} finally {
lock.unlock();
}
}
}
}
class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread w1=new Thread(ticket,"w1");
Thread w2=new Thread(ticket,"w2");
Thread w3=new Thread(ticket,"w3");
w1.start();
w2.start();
w3.start();
}
}
2. 死锁
每个人拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃自己拥有的资源。
死锁条件:两个以上的线程;至少两个锁;同步中嵌套同步
class MyLock {
public static Object objectA=new Object();
public static Object objectB=new Object();
}
class Boy extends Thread {
@Override
public void run() {
synchronized (MyLock.objectA){
System.out.println(Thread.currentThread().getName()+"拿到a");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyLock.objectB){
System.out.println(Thread.currentThread().getName()+"拿到b");
System.out.println(Thread.currentThread().getName()+"可以吃了...");
}
}
}
}
class Gril extends Thread {
@Override
public void run() {
synchronized (MyLock.objectB){
System.out.println(Thread.currentThread().getName()+"拿到b");
synchronized (MyLock.objectA){
System.out.println(Thread.currentThread().getName()+"拿到a");
System.out.println(Thread.currentThread().getName()+"可以吃了...");
}
}
}
}
class Demo3 {
public static void main(String[] args) {
Boy boy=new Boy();
Gril gril=new Gril();
boy.start();
gril.start();
}
}
3. 多线程在单例中的应用(*)
(1)
public class SingleTon {
private SingleTon() {
//禁止反射破解
synchronized (SingleTon.class) {
if (instance != null) {
throw new RuntimeException("不能使用反射创建对象");
}
}
}
//volatile:不稳定的,这里作用为保证线程可见性,禁止指令重排序
private static volatile SingleTon instance;
public static SingleTon getInstance() {
if (instance == null) { //double check.提高效率
synchronized (SingleTon.class) {
if (instance == null) { //判断为null
instance = new SingleTon();
}
}
}
return instance;
}
}
(2)静态内部类写法 (节省空间,不会有线程安全问题)
public class SingleTon2 {
private SingleTon2(){
}
static class Holder{
private static final SingleTon2 INSTACNE=new SingleTon2();
}
public static SingleTon2 getInstance(){
return Holder.INSTACNE;
}
}
(3)枚举写法(没有线程安全问题,反射破解问题)
public enum SingleTon3 {
INSTANCE;
public static SingleTon3 getInstance(){
return INSTANCE;
}
}