一. 线程核心概念
进程:在操作系统运行的程序就是---进程。 例如: 看视频。
一个进程可以有多个线程,如视频中 同时可以 听声音,看图像, 显示字幕。
main() 称之为主线程,为系统的入口点,用于执行整个程序。
在程序运行时,即使自己没有创建线程,后台也会存在多个线程,如gc线程,主线程。
继承Thread
1.创建一个新的执行线程有两种方法:
-
将一个类声明为一个Thread的子类。这个子类应该重写run类的方法Thread。
-
然后实例化此类,并调用 *statrt()*方法。
-
`public class StartThread extends Thread{
//重写run
public void run(){
for(int i = 0; i<20; i++){
System.out.println(“一边听歌”);
}
}public static void main(String[] args){
//创建子类对象
StartThread st = new StartThread();
//启动
st.start() ; //并不保证会立即执行,由CPU调用。
for(int i=0 ; i < 20; i++){
System.out.println(“一边coding”)
}
}
}`
注意:直接调用run方法,不是开启多线程,而是普通方法的调用。
-
//例子:图片下载
创建第一个类用来写下载图片的代码
public class WebDownLoad {
public void download(String url, String name){
FileUtils.copyURLToFile(new URL(url), new File(name));
}
}
//第二个来继承Thread 实现同时下载多个图片
public class ThreadDownload extends Thread{
private String url; //图片地址
private String name; //保存文件名称
public ThreadDownLoad(String url, String name){
this.url = url;
this.name = name;
}
//重写run方法
public void run(){
//调用下载类中的方法
WebDownLoad wd = new WebDownLoad();
wd.download(url,name);
}
public static void main(String[] args){
ThreadDownLoad td1 = new ThreadDownLoad( "链接”,“保存文件名”);
ThreadDownLoad td2 = new ThreadDownLoad( "链接”,“保存文件名”);
ThreadDownLoad td3 = new ThreadDownLoad( "链接”,“保存文件名”);
//启动
td1.start();
td2.start();
td3.start();
}
}
2.声明一个实现Runnable接口的类。然后重写run方法。
需要借助Thread类来调用start()方法。new Thread§.start() 。 p 为实例化对象。
public class StartRun implements Runnable{
//重写run
public void run(){
for(int i= 0; i<20; i++){
System.out.println("一边听歌");
}
}
public static void main(String[] args){
//创建实现类对象
StartRun sr = new StartRun();
//创建代理类对象
Thread t = new Thread(sr);
//启动
t.start();
// 其中 上述这些对象 只用一次的话,可以用匿名对象
new Thread(new StartRun()).start();
for(int i=0 ; i < 20; i++){
System.out.println("一边coding")
}
}
}
//例子: 模拟抢票
public class Web12306 implements Runnable{
private int ticketCounts = 99;
public void run(){
while(true){
if(ticketCounts <0){
break;
}
System.out.println(Thread.CurrentThread().getName() + "--->" + ticketCounts --);
}
}
public static void main(String[] args){
//创建实现类对象 一个资源
Web12306 w1 = new Web12306();
System.out.println(Thread.CurrentThread().getName() );
//创建代理类对象 多个代理
new Thread(w1,"骂出").start();
new Thread(w1,"马处").start();
new Thread(w1,"啊啊").start();
}
}
//例子: 龟兔赛跑
public class Racer implements Runnable{
private String winner ;
public void run(){
for(int steps =1; steps <=100 ; steps++){
//模拟休息
if(Thread.currentThread().getName() .equals("rabbit") && steps%10 ==0){
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + "----->" + winner);
//判断是否结束
boolean flag = gameOver(steps);
if(flag){
break;
}
}
public boolean gameOver(int steps){
if(null != winner){ //如果已经有胜利者
return true;
}else{
if(100 == steps){
winner = Thread.currentThread().getName();
System.out.println("Winner" + "--->" + Winner);
return true;
}
}
return false;
}
public static void main(String[] args){
Racer r = new Racer();
new Thread(r,"tortoise").start();
new Thread(r,"rabbit").start();
}
}
.
创建线程方式三
以下载图片例子进行讲解
1.创建目标对象: CDownLoad cd = new CDownLoad(“图片地址”,“文件名称”);
2.创建执行服务:ExecutorService ser = Executors.newFilxedThreadPool(1);
3. 提交执行: Future result1 = ser.submit(cd1);
4. 获取结果: boolean r1 = result1.get();
5. 关闭服务:ser.shutdownNow();
public class CThreadDownload2 implements Callable<Boolean>{
private String url; // 远程路径
private String name; //存储名字
public CThreadDownload2(String url,String name) {
this.url = url;
this.name = name;
}
public Boolean call() {
WebDownload wd = new WebDownload();
wd.download(url, name);
return true;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
CThreadDownload2 td1 = new CThreadDownload2("http://pic16.nipic.com/20111006/6239936_092702973000_2.jpg","111.jpg");
CThreadDownload2 td2 = new CThreadDownload2("http://pic25.nipic.com/20121112/9252150_150552938000_2.jpg","222.jpg");
CThreadDownload2 td3 = new CThreadDownload2("http://pic1.win4000.com/wallpaper/c/53cdd1f7c1f21.jpg","333.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> results1 = ser.submit(td1);
Future<Boolean> results2 = ser.submit(td2);
Future<Boolean> results3 = ser.submit(td3);
//获取结果
boolean r1 = results1.get();
boolean r2 = results2.get();
boolean r3 = results3.get();
//关闭服务
ser.shutdownNow();
}
}
静态代理
public class StaticProxy {
public static void main(String[] args){
new WeddingCompany(new You).happyMarry();
}
}
interface Marry {
public void happyMarry();
}
//真实角色
class You implements Marry{
//重写
public void happyMarry(){
System.out.println("你和你心爱的女人结婚了");
}
}
//代理角色
class WeddingCompany implements Marry{
//真实角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
//重写
public void happyMarry(){
ready();
this.target.happyMarry();
after();
}
public ready(){
System.out.println("准备主卧");
}
public after(){
System.out.println("闹洞房");
}
}
lambda表达式
一:带参数
public class LambdaTest02 {
public static void main(String[] args) {
lLove love =(int a) -> {
// TODO Auto-generated method stub
System.out.println("i like lambda" + "-- >" +a);
};
love.lambda(2);
//简化 类型拿掉
love = (a) -> {
System.out.println("i like lambda" + "-- >" +a);
};
love.lambda(3);
//简化 括号可以拿掉
love = a -> {
System.out.println("i like lambda" + "-- >" +a);
};
love.lambda(4);
//简化 如果只有一行代码 花括号也可以省略
love = a ->System.out.println("i like lambda" + "-- >" +a);
love.lambda(54);
}
}
interface lLove {
void lambda(int a);
}
//外部类
class Love implements lLove{
@Override
public void lambda(int a) {
// TODO Auto-generated method stub
System.out.println("i like lambda" + "-- >" +a);
}
}
二:有返回值
public class LambdaTest03 {
public static void main(String[] args) {
llnterest ll = (int a ,int b) ->{
System.out.println("i like lambda---> " + a);
return a+b;
};
ll.lambda(2, 3);
//简化 类型拿去 全部
ll = ( a , b) ->{
System.out.println("i like lambda---> " + a);
return a+b;
};
ll.lambda(32, 3);
//两行代码 不可去掉花括号
}
}
interface llnterest{
int lambda(int a, int b);
}
///外部类
class lnterest implements llnterest{
@Override
public int lambda(int a, int b) {
// TODO Auto-generated method stub
System.out.println("i like lambda---> " + a);
return a+b;
}
}
线程状态
进入就绪状态的四种方法:1.start方法。 2.阻塞事件解除。3.yield方法。4.jvm将cpu从本地线程切换到其他线程。
进入到阻塞状态的四种方法: 1.sleep方法。 2. wait方法。3.join方法。 4.IO流阻塞:read write
线程停止
一:线程正常执行完毕—》次数
二:外部干涉—》加入标识
不要使用stop destroy ----->不安全
public class TerminateThread implements Runnable{
//1.加入标识
private boolean flag = true;
private String name ;
//构造方法
public TerminateThread(String name){
this.name = name;
}
//重写run
public void run(){
//2.关联标识
int i = 0;
while(flag){
System.out.println(name + "---->" + i++);
}
}
//3.提供外部方法,改变标识
public void terminate(){
this.flag = false;
}
public static void main(String[] args){
TerminateThread tt = new TerminateThread("shuai");
new Thread(tt).start();
//
for(int i=0; i<100; i++){
if( i == 88){
tt.terminate();//线程终止
System.out.println("tt gameover");
}
System.out.println("main----->" + i);
}
}
}
sleep
sleep(时间); 指定当前线程阻塞的毫秒数。
sleep存在异常InterruptedException
sleep时间达到后,线程进入就绪状态。
sleep 可以模拟网络延迟、倒计时。
每个对象都有一个锁,sleep不会释放锁
模拟网络延时----抢票
public class BlockedSleep {
//一份资源
Web12306 web = new Web12306();
//多个代理
new Thread(web,"c1").start();
new Thread(web,"c2").start();
new Thread(web,"c3").start();
}
class Wed12306 implements Runnable{
//票数
private int ticketCounts = 99;
//重写run
while(true){
if(ticketCounts <0 ){
break;
}
//模拟延时
Thread.sleep(200); // 扑捉异常
System.out.println(Thread.currentThread().getName() + "---->"+ticketCounts--);
}
}
sleep模拟倒计时
public class BlockedThread02 {
public static void main(String[] args){
//
Date endTime = new Date(System.currentTimeMills() + 1000*10);
long end = endTime.getTime();
while(true){
//打印 按照格式 把时间转为字符串
System.out.printlin(new SimpleDateFormat("mm:ss").format(endTime));
Thread.sleep(1000);//延迟一秒
//减去一秒
endTime = new Date(endTime.getTime()-1000);
//倒计时总共10秒
if(end-10000 > endTime){
break;
}
}
}
}
yield
1.礼让线程,让当前正在执行线程暂停
2.不是阻塞线程,而是将线程从运行状态转入就绪状态
3.让cpu调度器重新调度
主线程内礼让
public YieldThread implements Runnable{
public static void main(String[] args){
new Thread(()-> {
for(int i =0; i< 100; i++){
System.out.println("lambda..." + i);
}
}).start();
for(int i=0; i < 100; i++){
if(i %20 == 0){
Thread.yield(); //main礼让
}
System.out.println("main..." + i);
}
}
}
join
join合并线程,插队线程。
待此线程执行完成后,再执行其他线程,其他线程阻塞。
主线程内演示join
public class JoinThread01 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for(int i =0; i<100; i++) {
System.out.println("lambda..." + i);
}
});
t.start();
for(int i=0; i<100; i++) {
if(i == 20) {
t.join(); //插队 main被阻塞
}
System.out.println("main..."+i);
}
}
}
//例子:老爸让儿子买烟
public class JoinThread {
System.out.println("老爸让儿子买烟的故事“);
new Thread(new Father()).start();
}
class Father extends Thread{
System.out.println("老爸想抽烟,发现没了“);
System.out.println("老爸让儿子去买“);
Thread t = new Thread(new Son());
t.start();
t.join(); //father被阻塞
System.out.println("老爸接过烟,把零钱给了儿子“);
}
class Son extends Thread{
System.out.println("儿子接过烟,去买烟“);
System.out.println("路上看见游戏厅,玩了10秒“);
for(int i= 1; i<=10; i++){
System.out.println(i +"秒过去了。。。”);
Thread.sleep(1000);
}
System.out.println("想起来买烟,赶紧去买“);
}
观察线程状态
public class AllState {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()-> {
for(int i =0; i <5;i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("11111");
});
//观察状态
State s = t.getState();
System.out.println(s); //NEW
t.start();
s = t.getState();
System.out.println(s); //RUNNABLE
while(s != Thread.State.TERMINATED) {
Thread.sleep(200);
s = t.getState();
System.out.println(s); //TIMED_WAITING
}
s = t.getState();
System.out.println(s); //TERMINATED
}
}
线程优先级
线程的优先级用数字表示,范围 1 —》 10
Thread.MIN_PRIORITY =1;
Thread.MAX_PRIORITY =10;
Thread.NORM_PRIORITY =5;
使用下面的方法可以或的和设置优先级
int getPriority();
void setPriority(int Priority);
优先级的设定建议在start()调用前
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高,后调用优先级低的线程。
public class PriorityThread {
public static void main(String[] args) {
MyPriority my = new MyPriority();
Thread t1 = new Thread(my,"c1");
Thread t2 = new Thread(my,"c2");
Thread t3 = new Thread(my,"c3");
Thread t4 = new Thread(my,"c4");
Thread t5 = new Thread(my,"c5");
//设置优先级在start前面
t1.setPriority(3);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(3);
t4.setPriority(Thread.MAX_PRIORITY);
t5.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + " --- >" +Thread.currentThread().getPriority());
}
}
守护线程
线程分为用户线程和守护线程
虚拟机必须保证用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如后台记录操作日志、监控内存使用等。
守护线程是为用户线程服务的
public class DaemonThread {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t = new Thread(god);
t.setDaemon(true); //将用户线程调整到守护线程
t.start();
new Thread(you).start();
}
}
class You implements Runnable{
public void run() {
for(int i=0; i <365; i++) {
System.out.println("happy life ....");
}
System.out.println("00=00");
}
}
class God implements Runnable{
public void run() {
for(;true;) {
System.out.println("God ....");
}
}
}
其他常用方法
isAlive() 判断线程是否还活着,即线程是否还未终止
setName() 给线程起个名字
getName() 获取线程的名字
currentThread() 取得当前正在运行的线程的对象,也就是获取自己本身
线程同步
并发:同一个对象多个线程同时操作。
例如:同时购买一张票
//不安全例子: 你和对象一起取钱
public class UnsafeThread{
public static void main (String[] args){
Account account = new Account(100,"结婚礼金”);
Drawing you = new Drawing(account, 80, "可悲的你");
Drawing wife = new Drawing(account,90,"高兴的她");
you.start();
wife.start();
}
}
//账户
class Account{
int money ; //金额
String name; //账户名称
public Account(int money, String name){
this.money = money;
this.name = name;
}
}
//模拟取钱
class Drawing extends Thread{
Account account; //账户
int drawingMoney; //取多少
int packetToale; //口袋里有多少
public Drawing(Account accouny, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run(){
if(account.money - drawingMoney <0){
return;
}
Thread.sleep(1000);
account.money -= drawingMoney ;
packetToale += drawingMoney ;
System.out.println(this.getName()+"----->账户余额" +account.money );
System.out.println(this.getName()+"----->口袋里的钱" +packetTotal );
}
}
线程::在访问时加入了锁机制 ,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
1.一个线程持有多=锁,会导致其他需要此锁的线程挂起。
2. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
3. 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。
同步方法
public synchronized void method(int args) {}
这个锁 所得是 对象的资源
//例子:抢票
public class SynThread{
public static void main(String[] args){
//一份资源
Web12306 web = new Web12306();
//多个代理
Thread t1 = new Thread(web,"c1");
Thread t2 = new Thread(web,"c2");
Thread t3 = new Thread(web,"c3");
t1.start();
t2.start();
t3.start();
}
}
class Web12306 implements Runnable{
private int ticketCounts = 19;//票数
private boolean flag = true; //标识
//重写run
public void run(){
while(flag){
//模拟延时
Thread.sleep(100);
test();
}
}
public **synchronized** void test(){
if(ticketCounts <= 0){
flag = false;
return;
}
//模拟延时
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "---->" + ticketCounts--);
}
}
同步块
synchronized (obj), obj 称之为同步监视器
obj 可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器是this即该对象的本身,或class即类的模子。
取钱的例子:使用同步块更准确的锁定
public class SynTest02 {
public static void main(String[] args) {
Account1 account = new Account1(1000,"结婚礼金");
SynBany you = new SynBany(account,80,"悲伤的你");
SynBany wife = new SynBany(account,90,"happy的她");
you.start();
wife.start();
}
}
//账户
class Account1 {
int money; //金额
String name; //名称
public Account1(int money, String name) {
super();
this.money = money;
this.name = name;
}
}
//
class SynBany extends Thread{
Account1 account; //账户
int drawingMoney; //要取的钱
int packetTotal; //口袋里的钱
public SynBany(Account1 account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run() {
try {
test();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//目标准确 锁定account对象
public void test() throws InterruptedException {
if(account.money <=0) {
return ;
}
synchronized(account) {
if(account.money - drawingMoney <0) {
return;
}
//模拟延时
Thread.sleep(1000);
account.money -=drawingMoney;
packetTotal +=drawingMoney;
System.out.println(Thread.currentThread().getName() + "--->账户金额:" + account.money);
System.out.println(Thread.currentThread().getName() + "--->口袋金额:" + packetTotal);
}
}
}
线程安全:尽可能的锁定合理的范围(不是代码 指的是数据的完整性)
抢票的例子
public class SynTest03{
public static void main(String[] args){
//一份资源
Web1230612 web = new Web1230612();
//多个代理
Thread t1 = new Thread(web,"c1");
Thread t2 = new Thread(web,"c2");
Thread t3 = new Thread(web,"c3");
t1.start();
t2.start();
t3.start();
}
}
class Web1230612 implements Runnable{
private int ticketCounts = 2;//票数
private boolean flag = true; //标识
//重写run
public void run(){
while(flag){
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
test05();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//double checking
public void test05() throws InterruptedException{
if(ticketCounts <= 0){ //考虑的是有没有票
flag = false;
return;
}
synchronized(this) {
if(ticketCounts <= 0){ //考虑的是最后一张票
flag = false;
return;
}
//模拟延时
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "---->" + ticketCounts--);
}
}
}
例子:快乐影院
public class HappyCammer {
public static void main(String[] args) {
//电影院可用票位
List<Integer> available = new ArrayList<Integer>();
available.add(1);
available.add(2);
available.add(3);
available.add(6);
available.add(7);
available.add(8);
//购买票位
List<Integer> seats1 = new ArrayList<Integer>();
seats1.add(1);
seats1.add(2);
//购买票位
List<Integer> seats2 = new ArrayList<Integer>();
seats2.add(5);
seats2.add(6);
seats2.add(7);
Cinemal cinemal = new Cinemal(available,"happyCinemal");
new Thread(new Audience(cinemal,seats1),"老王").start();;
new Thread(new Audience(cinemal,seats2),"老Li").start();
}
}
//电影院
class Cinemal{
List<Integer> available; //可用座位
String name; //名称
public Cinemal(List<Integer> available, String name) {
super();
this.available = available;
this.name = name;
}
public boolean bookTickets(List<Integer> seats) {
System.out.println("可用票位为"+ available);
List<Integer> copy = new ArrayList<Integer>();
copy.addAll(available);
//相减
copy.removeAll(seats);
//判断大小
if(available.size() - copy.size() != seats.size()) {
return false;
}
//成功
available = copy;
return true;
}
}
//观众
class Audience implements Runnable{
Cinemal cinemal; //电影院
List<Integer> seats ; //取的票
public Audience(Cinemal cinemal, List<Integer> seats) {
super();
this.cinemal = cinemal;
this.seats = seats;
}
@Override
public void run() {
synchronized(cinemal) {
boolean flag = cinemal.bookTickets(seats);
if(flag) {
System.out.println("出票成功"+ Thread.currentThread().getName()+"->位置为"+seats);
}else {
System.out.println("出票失败"+ Thread.currentThread().getName()+"票数不够");
}
}
}
}
例子:快乐火车票
public class Happy12306{
}
//顾客
class Passenger extends Thread{
int seats ; //票数
public (Runnable target, String name, int seats){
super(target,name);
this.seats = seats;
}
}
//火车票
class Web12306 implements Runnable{
int available; //可用票数
String name; //名称
public Web12306(int available, String name){
super();
this.available = available;
this.name = name;
}
//重写
public void run(){
Passenger p = (Passenger)Thread.currentThread();
boolean flag = this.bookTickets(p.seats);
if(flag){
System.out.println(Thread.currentThead().getName() + " ---->取的票数为:"+ p.seats);
}else{
System.out.println(Thread.currentThead().getName() + " 票数不够");
}
}
//
public syncgronized boolean bookTickets(int seats){
System.out.println("当前票数为” + available);
if(seats > available){
return false;
}
available -= seats;
return true;
}
}
多线程并发容器
CopyOnWriteArrayList
public class SynContainer {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for(int i=0; i< 1000;i++) {
new Thread(()-> {
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(10000);
System.out.println(list.size());
}
}
死锁
定义:多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。
某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的现象。
避免:不要在同一个代码块中,同时持有多个对象的锁。
生产者消费者模式
用代码演示一下管程法
public class CoTest01{
public static void main(String[] args){
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
//缓冲器对象
SynContainer container ;
public Productor(SynContainer container) {
super();
this.container = container;
}
//run
public void run(){
//开始生产
for(int i=0 ; i<100; i++) {
System.out.println("生产---》"+i+"个馒头");
container.push(new Steamedbun(i));
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
super();
this.container = container;
}
//run
public void run(){
for(int i =0; i<1000; i++}{
System.out.println("消费---》"+container.pop().id+"个馒头");
}
}
}
//缓存区
class SynContainer{
Steamedbun[] buns = Steamedbun[10];
int count;计数
//生产
public synchronized void push(Steamedbun bun){
//何时能生产
//空间满了 就不能生产
if(count == buns.length){
this.wait(); //阻塞, 等待消费者通知接触阻塞
}
//空间有了 就生产
buns[count] = bun;
count--;
this.notifyAll(); //解除消费者阻塞
}
//消费
public synchronized Steamedbun pop(){
//何时能消费 存在数据
//不能消费 数据为0
if( count == 0){
this.wait(); //等待阻塞 等生产者通知消费者解除阻塞
}
//何时能消费 有数据
count--; //从最后一个取数据
Steamedbun bun = buns[count];
存在空间了,通知生产者可以生产了
this.notifyAll();
return bun;
}
}
//数据:这里设为馒头
class Steamedbun{
int id; //馒头编号
public Steamedbun(int id){
this.id = id;
}
}
生产者消费者模式的实现方式二:
- 协作模型:生产者消费者实现方式二:信号灯法
*借助标识位
public class CoTest02 {
public static void main(String[] args) {
Tv tv = new Tv();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者 表演者
class Player extends Thread{
Tv tv;
public Player(Tv tv) {
this.tv = tv;
}
public void run() {
for(int i=0; i<20;i++) {
if(i % 2 == 0) {
this.tv.play("奇葩书");
}else {
this.tv.play("太污了");
}
}
}
}
//消费者 观众
class Watcher extends Thread{
Tv tv;
public Watcher(Tv tv) {
this.tv = tv;
}
public void run() {
for(int i=0; i<20;i++) {
this.tv.watch();
}
}
}
//同一资源 电视
class Tv{
String voice;
//信号灯
//T 表示演员表演 观众等待
//F 表示观众观看 演员等待
boolean flag = true;
//表演
public synchronized void play(String voice) {
//演员等待
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("表演了:"+ voice);
this.voice = voice;
//变换标志位
this.flag = !this.flag;
//唤醒
this.notifyAll();
}
//观看
public synchronized void watch() {
//观众等待
if(flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("观看了:"+ voice);
this.voice = voice;
//唤醒
this.notifyAll();
//变换标志位
this.flag = !this.flag;
}
}
下面是高级主题
定时调度
某个有规律的时间干某件事
通知Timer 和 Timetask 我们可以实现定时启动某个线程
iava.util.Timer : 类似闹钟的功能,本身实现的就是一个线程。
java.util.TimerTask : 一个抽象类,该类实现了Runnable 接口,所以该类具备多线程的能力。
public class TimerTest01 {
public static void main(String[] args) {
Timer timer = new Timer();
//执行安排
//timer.schedule(new MyMask(),1000); //执行一次
//timer.schedule(new MyMask(),1000 ,200); //执行多次
Calendar cal = new GregorianCalendar(2019,9,19,16,41,10);
timer.schedule(new MyMask(),cal.getTime(),200); //指定时间
}
}
//任务类
class MyMask extends TimerTask{
@Override
public void run() {
for(int i =0; i< 2 ; i++) {
System.out.println("防空啊啊啊啊啊啊");
}
System.out.println("---------end---------");
}
}
HappenBefore
执行代码的顺序可能与编写代码不一致,即虚拟机优化代码顺序,则为指令重排happenBefore。
即:编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序的一种手段。
volatile
保证线程间变量的可见性,简单来说就是当线程A对变量X进行了修改后,在线程A后面执行的其他线程能看到变量X的变动。
规则:
线程对变量进行修改之后,要立即回写到主内存。
线程对变量读取的时候,要从主内存中读,而不是缓存。
volatile是不错的机制,但是volatile不能保证原子性。
用于保证数据的同步
* 保证数据的同步,也就是可见性
* @author admin
*
*/
public class volatileTest01 {
public volatile static int num =0;
public static void main(String[] args) throws InterruptedException {
new Thread(()-> {
while(0 == num) { //此处不要写代码
}
}).start();
Thread.sleep(1000);
num =1;
}
}
单例模式
懒汉式套路基础上加入并发控制,保证 在多线程环境下,对外存在一个对象。
1.构造器私有化----》避免外部new 构造器
2.提供私有的静态属性–》存储对象的地址
3.提供公共的静态方法–》获取属性
public class DoubleCheckedlocking{
//2.静态属性
private static volatile DoubleCheckedlocking instance;
//没有volatile 其他线程可能访问一个没有初始化的对象
//1.构造器私有化
private DoubleCheckedlocking{
}
//3.提供公共静态方法---获取属性
public static DoubleCheckedlocking getInstance(){
//再次检测
if(null != instance){ //避免不必要的同步,对已经存在过的对象
return instance;
}
synchronized(DoubleCheckedlocking.class){
if(instance == null){
instance = new DoubleCheckedlocking();
//1.开辟空间 2.初始化对象信息 3.返回对象的地址给引用
}
}
return instance;
}
public static void main(String[] args) {
Thread t = new Thread(()-> {
System.out.println(DoubleCheckedLocking.getInstance());
});
t.start();
System.out.println(DoubleCheckedLocking.getInstance());
}
}
ThreadLocal
ThreadLocal能存放一个线程级别的变量,其本身能够被多个线程共享使用,并且又能达到线程安全的目的。
常用方法:get/set/initialValue方法
建议定义为private static
最常用的就是为每个线程绑定一个数据库连接。
InheritableThreadLocal:继承上下文 环境的数据,拷贝一份给子线程。
public class ThreadLoacalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<> ();//初始时
更改初始值
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
protected Integer initialValue(){
return 200;
}
};
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->300);
public static void main(String[] args) {
//获取值
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
//设置值
threadLocal.set(90);
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
}
}
可重用锁
锁是可以延续使用的
ReentranLock
原子操作
public class CAS {
//库存
private static AtomicInteger stock = new AtomicInteger(5);
public static void main(String[] args) {
for(int i=0; i<5 ;i++) {
new Thread(()-> {
//模拟网络延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Integer left = stock.decrementAndGet();
if(left <1) {
System.out.println("抢完了");
}
System.out.println(Thread.currentThread().getName() + "--抢到了"+ "-->剩"+ left);
}).start();
}
}
}