JUC : java.util.concurrent
1.线程 操作 资源(类)
2.高内聚 低耦合
lock代替synchronized;
synchronized : 锁提供了对共享资源的独占访问,一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁 ;
lock提供了更加功能的锁;
lock = synchroizesd++
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
模板 :
Thread 的方法,一直是用:
new Thread ( new Runable(){
xxx . . .
).start ;
3个买票窗口卖30张票:
package com.atguigu;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Ticket{
private int number = 30;
private Lock lock = new ReentrantLock();
public void sale(){
lock.lock();
try {
if (number > 0) {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"卖出去:\t"+(number--)+"\t还剩下:"+number);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ThreadText {
public static void main(String[] args) {
final Ticket ticket = new Ticket();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 40; i++) {
ticket.sale();
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 40; i++) {
ticket.sale();
}
}
},"BB").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 40; i++) {
ticket.sale();
}
}
},"CC").start();
}
}
获取多线程的3种方法:
1.继承Thread类,重写run 方法;
class xx extends Thread{
public void run(){
Thread.sleep(1000) //线程休眠1000毫秒,sleep使线程进入Block状态,并释放资源
}}
开启线程:
对象.start() //启动线程,run函数运行
2.实现Runnable接口,重写run方法;
开启线程:
Thread t = new Thread(对象) //创建线程对象
t.start()
3.实现 Callable 接口,重写call 方法 ;
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Runnable有几点不同:
①Callable规定的方法是call(),而Runnable规定的方法是run().
②Callable的任务执行后可返回值,而Runnable的任务是不能返回值的
③call()方法可抛出异常,而run()方法是不能抛出异常的。
④运行Callable任务可拿到一个Future对象,Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等
待计算的完成,并检索计算的结果.通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果
Thread -->runnable
Thread–>runnable–>RunnableFuture–>FutureTask–>FutureTask(Callable,Callable)
Callable接口演示:
package com.atguigu;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.err.println("你看不住我的,我已经进来了");
return 200;
}
}
public class ThreadDeom {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
线程的同步和通信
生产者消费者+等待+通知唤醒
package com.atguigu;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
- @author 小6
- 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1,
- 实现交替,来5轮,变量初始值为零。
*/
class ShareDate {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException{
lock.lock();
try {
while (number != 0) {
condition.await();
}
++number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
} catch (Exception e) {
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException{
lock.lock();
try {
while (number == 0) {
condition.await();
}
--number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ThreadDeom2 {
public static void main(String[] args) {
final ShareDate shareDate = new ShareDate();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(200);
shareDate.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(300);
shareDate.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"BB").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(400);
shareDate.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"CC").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(500);
shareDate.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"DD").start();
}
}
8锁的例子:
*1 标准普通调用,先打印苹果还是android
*2 新添加Thread.sleep(4000),先打印苹果还是android
*3 新增加Hello方法,先打印苹果还是hello
*4 有两部手机,先打印苹果还是android
*5 两个静态同步方法,同一部手机,先打印苹果还是android
*6 两个静态同步方法,有两部手机,先打印苹果还是android
*7 一个静态同步方法,一个普通同步方法,同一部手机,先打印苹果还是android
*8 一个静态同步方法,一个普通同步方法,有两部手机,先打印苹果还是android
8锁 :
一个类资源对象里面如果有多个synchronized方法, 某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前类资源,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化(静态的方法锁的是类对象,非静态方法锁的是类资源)
所有的非静态同步方法用的都是同一把锁——实例对象本身,
也就是说如果一个实例对象的非静态同步方法获取锁后,
该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,
只要它们同一个类的实例对象!
按序接力例子:A–>B–>C
package com.atguigu.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyResource
{
private int number = 1;
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void loopAA(int totalLoop) throws InterruptedException
{
lock.lock();
try
{
//判断
while(number != 1)
{
condition1.await();
}
//干活
for (int i = 1; i <=5; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t当前是第:"+totalLoop);
}
//唤醒+改标记
number = 2;
condition2.signal();
}
catch (Exception e)
{
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void loopBB(int totalLoop) throws InterruptedException
{
lock.lock();
try
{
//判断
while(number != 2)
{
condition2.await();
}
//干活
for (int i = 1; i <=10; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t当前是第:"+totalLoop);
}
//唤醒+改标记
number = 3;
condition3.signal();
}
catch (Exception e)
{
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void loopCC(int totalLoop) throws InterruptedException
{
lock.lock();
try
{
//判断
while(number != 3)
{
condition3.await();
}
//干活
for (int i = 1; i <=15; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t当前是第:"+totalLoop);
}
//唤醒+改标记
number = 1;
condition1.signal();
}
catch (Exception e)
{
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
/**
*
* @author zhouyang
* 备注:多线程之间按顺序调用,实现A->B->C
* 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次
* 接着
* AA打印5次,BB打印10次,CC打印15次
*
* 共计来20轮
*/
public class ThreadDemo4
{
public static void main(String[] args)
{
final MyResource myResource = new MyResource();
new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=20; i++)
{
try
{
myResource.loopAA(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=20; i++)
{
try
{
myResource.loopBB(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
},"BB").start();
new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=20; i++)
{
try
{
myResource.loopCC(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println();
}
}
},"CC").start();
}
}