1.实现线程安全的是那种方法:
用于解决多线程的安全的三种方法:同步方法、同步代码块、同步锁
其中同步方法和同步代码块用的是synchronized修饰的,它是属于隐式锁。
在 JDK 1.5之后出现同步锁Lock(显示锁)
注意:需要通过lock()的方法进行上锁,必须通过unlock()方法释放锁。
- 在Java 5.0之前,协调共享对象的访问时,可以使用的机制只有synchronized和volatile。Java5.0后增加了一些新的机制,但并不是一种代替内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。
- ReentrantLock实现了Lock接口,提供了与synchronized提供了更高的处理锁的灵活性。
public class TestLock {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"1号").start();
new Thread(ticket,"2号").start();
new Thread(ticket,"3号").start();
}
}
class Ticket implements Runnable{
private int tick = 100;
private Lock lock= new ReentrantLock();//多态的提现
@Override
public void run() {
while (true) {
lock.lock();//此处为上锁
try {
if (tick > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"完成售票,余票为:"+ --tick);
}
} finally {
lock.unlock();//释放锁
}
}
}
}
2.生产者和消费者案例:
1.最基本的例子:
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor productor = new Productor(clerk1);
Consumer consumer = new Consumer(clerk1);
new Thread(productor,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
//店员
class Clerk1{
private int product=0;//货物
//进货
public synchronized void get(){
if(product >=10){
//如果产品满了之后,该线程需要进行等待
System.out.println("产品已满");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else{
//如果存在库存的话,将会唤醒所有的线程
System.out.println(Thread.currentThread().getName()+" : "+ ++product);
this.notifyAll();
}
}
//卖货
public synchronized void set(){
if (product <=0){
//如果没有货物的话,当前线程需要进行等待
System.out.println("缺货,需要进货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+" : "+ --product);
this.notifyAll();
}
}
}
//生产者
class Productor implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
public Productor(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
//消费者
class Consumer implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk1.set();
}
}
public Consumer(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
注意:上述的方法是存在一个问题的,就是程序会在一直的运行中,因为有个else判断。
案例2:在1的基础上进行修改。
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor productor = new Productor(clerk1);
Consumer consumer = new Consumer(clerk1);
new Thread(productor,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
//店员
class Clerk1{
private int product=0;//货物
//进货
public synchronized void get(){
if(product >=10){
//如果产品满了之后,该线程需要进行等待
System.out.println("产品已满");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果存在库存的话,将会唤醒所有的线程
System.out.println(Thread.currentThread().getName()+" : "+ ++product);
this.notifyAll();
}
//卖货
public synchronized void set(){
if (product <=0){
//如果没有货物的话,当前线程需要进行等待
System.out.println("缺货,需要进货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : "+ --product);
this.notifyAll();
}
}
//生产者
class Productor implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
public Productor(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
//消费者
class Consumer implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk1.set();
}
}
public Consumer(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
注意:根据问题1,进行了改进,就是把else删除,这样的话就会让线程结束。从而让真个程序结束。
案例3:在案例2的基础上,进行了整改:
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor productor = new Productor(clerk1);
Consumer consumer = new Consumer(clerk1);
new Thread(productor,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
//店员
class Clerk1{
private int product=0;//货物
//进货
public synchronized void get(){
while (product >=10){
//如果产品满了之后,该线程需要进行等待
System.out.println("产品已满");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果存在库存的话,将会唤醒所有的线程
System.out.println(Thread.currentThread().getName()+" : "+ ++product);
this.notifyAll();
}
//卖货
public synchronized void set(){
while (product <=0){
//如果没有货物的话,当前线程需要进行等待
System.out.println("缺货,需要进货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : "+ --product);
this.notifyAll();
}
}
//生产者
class Productor implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
public Productor(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
//消费者
class Consumer implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk1.set();
}
}
public Consumer(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
问题:如果出现两个消费者和生产者的话,在线程等待或者唤醒,会把所有的都唤醒,不能唤醒指定的线程,所以这样也是存在问题的。
案例4:使用lock同步锁完成(Condition控制线程通信)
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor productor = new Productor(clerk1);
Consumer consumer = new Consumer(clerk1);
new Thread(productor,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
//店员
class Clerk1{
private int product=0;//货物
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//进货
public void get(){
//上锁
lock.lock();
try {
while (product >=10){
//如果产品满了之后,该线程需要进行等待
System.out.println("产品已满");
try {
condition.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果存在库存的话,将会唤醒所有的线程
System.out.println(Thread.currentThread().getName()+" : "+ ++product);
condition.signalAll();//唤醒所有的线程
}finally {
//释放资源
lock.unlock();
}
}
//卖货
public void set(){
lock.lock();
try {
while (product <=0){
//如果没有货物的话,当前线程需要进行等待
System.out.println("缺货,需要进货");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" : "+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
//生产者
class Productor implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
public Productor(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
//消费者
class Consumer implements Runnable{
Clerk1 clerk1 = new Clerk1();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk1.set();
}
}
public Consumer(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
}
- Condition接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用Object.wait访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个Lock可能与多个Condition对象关联。为了便面兼容性问题,Condition方法的名称与对应的Object版本中的不同。
- 在Condition对象中,与wait、notify和notifyAll方法对应的分别是await\signal \signalAll.
- Condition实例实质上被绑定到一个锁上。要为特定的Lock实例获取Condition实例,使用的是newCondition()方法。
3.线程按序交替:
问题:编写一个程序,开启3个线程,这三个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要输出的要求必须按顺序显示。
public class TestABCAlternate {
public static void main(String[] args) {
AlternateDemo alternateDemo = new AlternateDemo();
//执行A
new Thread(new Runnable() {//使用的是内部类的形式
@Override
public void run() {
//打印10遍
for (int i = 0; i < 10; i++) {
alternateDemo.loopA(i);
}
}
},"A").start();
//执行B
new Thread(new Runnable() {//使用的是内部类的形式
@Override
public void run() {
//打印10遍
for (int i = 0; i < 10; i++) {
alternateDemo.loopB(i);
}
}
},"B").start();
//执行C
new Thread(new Runnable() {//使用的是内部类的形式
@Override
public void run() {
//打印10遍
for (int i = 0; i < 10; i++) {
alternateDemo.loopC(i);
System.out.println("-------------------");
}
}
},"C").start();
}
}
class AlternateDemo{
private int number=1;//用来记录正在执行的线程的标记
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
//A
public void loopA(int totalLoop){
//上锁
lock.lock();
try {
//1.判断值
if (number != 1){
//如果number的值不为1的话,线程就要进入等待状态
condition1.await();
}
//2.打印
for (int i = 1 ; i <=1 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
//3.唤醒
number =2;
condition2.signal();//唤醒指定的线程
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
//A
public void loopB(int totalLoop){
//上锁
lock.lock();
try {
//1.判断值
if (number != 2){
//如果number的值不为1的话,线程就要进入等待状态
condition2.await();
}
//2.打印
for (int i = 1 ; i <=1 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
//3.唤醒
number =3;
condition3.signal();//唤醒指定的线程
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
//A
public void loopC(int totalLoop){
//上锁
lock.lock();
try {
//1.判断值
if (number != 3){
//如果number的值不为1的话,线程就要进入等待状态
condition3.await();
}
//2.打印
for (int i = 1 ; i <=1 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
//3.唤醒
number =1;
condition1.signal();//唤醒指定的线程
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
主要是通过线程通信的原理,来唤醒或者等待指定的线程,这样就可以达到按照顺序输出。
4.读写锁(ReadWriteLock)
- ReadWriteLock维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个reader线程同时保持。写入锁是独占的。
- ReadWriteLock读取操作通常不会改变共享资源,但是执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变形可以完全不需要考虑加锁操作。
/*
* 1.ReadWriteLock:读写锁
*
* 写写/读写 需要“互斥”
* 读读 不需要互斥
*/
public class TestReadWriteLock {
public static void main(String[] args) {
ReadWriteLockDemo tw = new ReadWriteLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
tw.set((int)(Math.random()*101));
}
},"写").start();
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
tw.get();
}
},"读").start();
}
}
}
class ReadWriteLockDemo{
private int number = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
//读
public void get() {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " : " + number);
} finally {
lock.readLock().unlock();
}
}
//写
public void set(int number) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName());
this.number = number;
} finally {
lock.writeLock().unlock();
}
}
}
5.八种锁打印出的不同值
/*
* 1.两个普通同步方法,两个线程,标准打印//one two
* 2.新增Thread.sleep()给getOne(),打印 //one two
* 3.新增普通方法getThree(),打印://three one two
* 4.两个普通同步方法,两个Number对象,打印://two one
* 5.修改getOne()为静态同步方法,打印://two one
* 6.修改这两个方法均为静态同步方法,同一个Number对象,打印://one two
* 7.一个静态同步方法,一个非静态同步方法,两个Number 打印:one two
* 8.两个静态同步方法,两个对象打印:one two
*
* 线程八锁的关键:
* 1.非静态方法的默认为this,静态方法的锁为对应的Class实例
* 2.某一个时刻内,只有一个线程持有锁,无论几个方法。
*/
public class TestThread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number1 = new Number();
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//number.getTwo();
number1.getTwo();
}
}).start();
// new Thread(new Runnable() {
//
// @Override
// public void run() {
// number.getThree();
// }
// }).start();
}
}
class Number {
public static synchronized void getOne() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("one");
}
public static synchronized void getTwo() {
System.out.println("two");
}
public void getThree() {
System.out.println("three");
}
}