Runnable 和 Thread的关系
- Runnable是接口,Thread是一个类
- Thread是实现了Runnable接口的类
- 尽量用Runnable,因为接口继承比类的继承更加面向对象
线程锁的方式
简单的方式就是用synchronized,可以修饰方法,代码块 静态方法,对象
如果修饰的是静态方法,该类的所有方法都是用的同一把锁
如果修饰的是非静态的,锁属于这个对象
更细粒度的控制是使用Lock,ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
public static void main(String[] args) throws InterruptedException {
Seller seller = new Seller(20);
new Thread(seller).start();
new Thread(seller).start();
new Thread(seller).start();
}
/**
** 在竞争的过程中会出现超卖的情况,
** 因为while没有锁住,几个线程在锁住票之前已经持有票了,票的数量不能在锁外去读
**/
public static class Seller implements Runnable {
private int tickCount;
public Seller(int count) {
this.tickCount = count;
}
@Override
public void run() {
while (tickCount >0 ) {
synchronized (Seller.class) {
System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
tickCount--;
/**
* 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
打印出来的结果:超卖了一张
Thread-0 : 卖出票 20
Thread-0 : 卖出票 19
Thread-2 : 卖出票 18
Thread-2 : 卖出票 17
Thread-1 : 卖出票 16
Thread-1 : 卖出票 15
Thread-1 : 卖出票 14
Thread-2 : 卖出票 13
Thread-2 : 卖出票 12
Thread-2 : 卖出票 11
Thread-2 : 卖出票 10
Thread-2 : 卖出票 9
Thread-2 : 卖出票 8
Thread-2 : 卖出票 7
Thread-2 : 卖出票 6
Thread-0 : 卖出票 5
Thread-0 : 卖出票 4
Thread-0 : 卖出票 3
Thread-0 : 卖出票 2
Thread-0 : 卖出票 1
Thread-2 : 卖出票 0
Thread-1 : 卖出票 -1
票的正确数量应该在锁里面读取,这样就不会超卖
public static class Seller implements Runnable {
private Integer tickCount;
public Seller(int count) {
this.tickCount = count;
}
@Override
public void run() {
while (true ) {
//所有对象公用一把锁
synchronized (Seller.class) {
//票的判断放到锁里面
if (tickCount < 0){
return;
}
System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
tickCount--;
/**
* 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
通过Lock实现同步互斥,因为Lock是接口,所以使用它的实现类来ReentrantLock,
/**
* 通过Lock来实现同步互斥
*/
public static class Seller implements Runnable {
private Integer tickCount;
private Lock lock;
public Seller(int count) {
this.tickCount = count;
this.lock = new ReentrantLock();
}
@Override
public void run() {
while (true ) {
try {
lock.lock();
//票的判断放到锁里面
if (tickCount < 0){
return;
}
System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
tickCount--;
/**
* 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
*/
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
线程之间的同步
之前都是互相抢的 太不友好了,现在来了个管理者,让大家一销售一次卖N张,卖完就收工让下个销售来,虽然是多线程,但是同一个时刻只有一个线程在工作,通过condition来实现
public static class SellerManager{
private ReentrantLock lock;
private Condition condition;
public SellerManager(){
this.lock = new ReentrantLock();
this.condition = lock.newCondition();
}
public void start(){
TicketCount tickerCount = new TicketCount(15);
while (tickerCount.getCount() > 0 ){
System.out.println("新的销售开始");
Seller seller = new Seller(tickerCount,5,lock,condition);
new Thread(seller).start();
try {
lock.lock();
//需要放在lock里面
System.out.println("开始等待第下一个人介入 " + lock.getHoldCount());
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.print(e);
}
}
}
}
/***
* 实现一个人卖5张
*/
public static class Seller implements Runnable {
private TicketCount tickCount;
private int canSellCount;
private Lock lock;
private Condition condition;
public Seller(TicketCount count,int canSellCount,Lock lock,Condition condition) {
this.tickCount = count;
this.canSellCount = canSellCount;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
while (true ) {
if (canSellCount == 0){
System.out.println(Thread.currentThread().getName() + " 卖票完成 " );
break;
}
System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount.getCount());
tickCount.sellOne();
canSellCount--;
/**
* 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.lock();
condition.signal();
lock.unlock();
}
}
private static class TicketCount{
private int count;
public TicketCount(int count){
this.count = count;
}
public void sellOne(){
this.count --;
}
public int getCount() {
return count;
}
}
}
打印的结果 :
新的销售开始
开始等待第下一个人介入 1
Thread-0 : 卖出票 15
Thread-0 : 卖出票 14
Thread-0 : 卖出票 13
Thread-0 : 卖出票 12
Thread-0 : 卖出票 11
Thread-0 卖票完成
新的销售开始
开始等待第下一个人介入 1
Thread-1 : 卖出票 10
Thread-1 : 卖出票 9
Thread-1 : 卖出票 8
Thread-1 : 卖出票 7
Thread-1 : 卖出票 6
Thread-1 卖票完成
新的销售开始
开始等待第下一个人介入 1
Thread-2 : 卖出票 5
Thread-2 : 卖出票 4
Thread-2 : 卖出票 3
Thread-2 : 卖出票 2
Thread-2 : 卖出票 1
Thread-2 卖票完成
这个修改下就可以实现线程B,C,D等待线程A完成后再进行工作.
单例的写法
/**
* Created by Administrator on 2018/4/17.
*/
/**
* 懒汉,对象是懒加载
* 线程不安全
*/
public class User {
private static User instance;
private User(){}
public static User getInstance(){
if (instance == null){
instance = new User();
}
return instance;
}
}
/**
* 懒汉,对象是懒加载
* 使用Synchronized修饰了静态方法,所以线程安全
* 但是方法级别的锁,效率低
*
*/
public class User {
private static User instance;
private User(){}
public static synchronized User getInstance(){
if (instance == null){
instance = new User();
}
return instance;
}
}
/**
* 饿汉
* 当class文件被加载的时候就创建了对象,获取的时候没有对象创建操作,不存在线程安全问题
* 如果没用到也占用着内存
*/
public class User {
private static User instance = new User();
private User(){
System.out.println("user 构造函数");
}
public static User getInstance(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("user getInstance");
return instance;
}
}
/**
* 同上,static代码块也是在类加载的时候运行
*/
public class User {
private static User instance = null;
static {
instance = new User();
}
private User(){}
public static User getInstance(){
return instance;
}
}
/**
* 这个静态内部类为什么可以延迟创建对象不太明白
*/
public class User {
private static class UserHolder{
static {
System.out.println("UserHolder");
}
private static User user = new User();
}
private User(){
System.out.println("user 构造函数");
}
static {
System.out.println("user static");
}
public static User getInstance(){
System.out.println("user getInstance");
return UserHolder.user;
}
}
/**
* 利用enum的特性
* enum里面的对象都是单例的
* 使用:User.instance.hello();
*/
public enum User{
instance;
public void hello(){
}
}
/**
* 双重校验锁
* 用的最多,检查了2次,第二次用synchronized进行了同步,因为只是对代码块修饰,比直接修饰方法效率高一点
*/
public class User{
private static User user = null;
private User(){}
public static User getInstance(){
if (user == null){
synchronized (User.class){
if (user == null){
user = new User();
}
}
}
return user;
}
}
网友评论